-
Notifications
You must be signed in to change notification settings - Fork 549
Description
In ClawRouter 0.12.23, injectModelsConfig() mutates ~/.openclaw/openclaw.json and deletes user-defined agents.defaults.models entries for blockrun/* IDs not in internal TOP_MODELS.
This creates a hard contradiction:
- Provider catalog is broad (41+ models; currently 45 model IDs in code),
- Effective callable allowlist is forcibly narrowed to
TOP_MODELS(16 entries).
Result: valid catalog model IDs become non-callable after startup/reload and fail with:
Model "<blockrun/model-id>" is not allowed
Environment
- OpenClaw:
2026.3.2 - ClawRouter:
0.12.23 - Plugin file (relative):
.openclaw/extensions/clawrouter/dist/index.js - Config file (relative):
.openclaw/openclaw.json
User-facing impact
- User-authored allowlist entries do not persist.
- Effective model policy regresses to top-set policy after plugin lifecycle.
- Error semantics imply user configuration fault while root cause is plugin-side mutation.
- Catalog discoverability and callable policy diverge.
- Persisted config is rewritten without user action beyond normal startup/restart.
Deterministic reproduction (copy/paste)
# 1) Add a non-top BlockRun model ID to allowlist
# ~/.openclaw/openclaw.json -> agents.defaults.models
# e.g. "blockrun/openai/gpt-5.4": {}
# 2) Restart gateway
openclaw gateway restart
# 3) Inspect effective allowlist
openclaw models status --json
# 4) Attempt to invoke the model
# Expect runtime rejection after pruning:
# Model "blockrun/openai/gpt-5.4" is not allowedObserved log pattern:
Added 1 models to allowlist (16 total)
Smart routing enabled (blockrun/auto)
Config overwrite: ~/.openclaw/openclaw.json (...)
Root-cause proof chain (high confidence)
A) Direct destructive logic exists
In dist/index.js, function injectModelsConfig():
const topSet = new Set(TOP_MODELS.map((id) => `blockrun/${id}`));
for (const key of Object.keys(allowlist)) {
if (key.startsWith("blockrun/") && !topSet.has(key)) {
delete allowlist[key];
needsWrite = true;
}
}This is explicit deletion of user allowlist entries outside TOP_MODELS.
B) Function is invoked during plugin registration
In same file, plugin register(api) calls:
api.registerProvider(blockrunProvider);
injectModelsConfig(api.logger);
injectAuthProfile(api.logger);Which makes the prune behavior part of ordinary lifecycle, not an edge-case repair pass.
C) Log signatures uniquely map to this code path
The strings below appear once in code and are emitted by this function:
Added ${addedCount} models to allowlist (${TOP_MODELS.length} total)Smart routing enabled (blockrun/auto)
These same signatures appear in runtime logs during observed reversion.
D) Version delta check confirms issue persists in latest
Compared installed package code in 0.12.13 –> 0.12.23:
injectModelsConfig()deletion behavior is unchanged.TOP_MODELSremains 16.- Broad catalog remains (45 IDs in code path).
Root-cause localization
- File:
dist/index.js - Function:
injectModelsConfig(logger) - Behavior: destructive normalization of
agents.defaults.modelsto curatedTOP_MODELS
Expected behavior
If a model ID is valid in provider catalog and explicitly present in agents.defaults.models, it should remain callable unless removed by user/admin policy.
Request to maintainers
Please clarify whether this behavior is intentional policy.
If intentional:
- expose as explicit opt-in config (e.g.,
enforceTopModelsAllowlist: true|false), default off, - document that direct model IDs outside curated set are not supported.
If unintentional:
- switch from destructive prune to additive merge (preserve user-defined keys, add defaults only).
Regression tests suggested
- User-added
blockrun/*allowlist entries survive gateway restart. - No plugin-driven deletion of persisted model policy unless explicit enforcement is enabled.
- Advertised provider model IDs are not blocked by implicit pruning.
- Error messaging distinguishes policy-pruned vs not-configured model states.