Summary
When an agent's tools.allow allowlist is non-empty but every name in it fails to resolve to a registered tool (the named tools are not built-in and not registered by any loaded plugin), the runtime emits a one-time stderr warning and then proceeds to run the agent with an empty function-call schema (systemPromptReport.tools.entries=[], schemaChars=0). The agent has no callable tools, but the model's output is unconstrained, so it tends to produce plausible-looking JSON or "tool result" text that downstream consumers cannot distinguish from real tool invocations. There is no tool-call span emitted, no provider-side function call recorded, and the run completes with stopReason: "stop" as if everything succeeded.
This is a quiet integrity failure: the agent claims to have done work it could not have done, and the only fail-loud signal is a stderr warning that's easy to miss in CI logs or Docker stdouts.
Severity
P1 — not a crash, but a category of silent-wrong-output that's hard for users to detect. I encountered it via a misconfigured sample MAS (sister bug: outshift-open/insightClaw#34): tools.allow=["query_db"] referenced a name nothing registers. The agent fabricated query_db responses for two consecutive turns without ever calling a tool, and the output looked correct enough that I had to cross-check the underlying HTTP service's access log to detect the hallucination.
Reproduction
- openclaw 2026.4.12 (1c0672b).
- Configure any agent with
tools.allow: ["nonexistent_tool"] in openclaw.json (no plugin defines it).
openclaw agent --agent <id> --json -m "Use nonexistent_tool to fetch data".
Observed:
- stderr:
[tools] agents.<id>.tools.allow allowlist contains unknown entries (nonexistent_tool). These entries won't match any tool unless the plugin is enabled.
- run report:
"tools": {"listChars": 0, "schemaChars": 0, "entries": []}.
- session JSONL: zero
toolCall events.
- final assistant text: a confidently-worded fabricated result.
I have a concrete real-world repro: two probes against a db agent whose tools.allow=["query_db"], both produced fabricated JSON that did not match the underlying HTTP service's actual response shape, and the service access log shows zero traffic from those probes.
Expected
Some fail-louder behavior. Options, roughly in order of preference:
- Refuse to start the run when an allowlist resolves to zero registered tools, returning an error like
agent <id> has no callable tools after allowlist filtering. Safest default.
- Fall back to the default tool set with a structured warning in the run report metadata (not just stderr) so observability tooling can flag it.
- At minimum: include the
tools.allow resolution mismatch as a structured field in systemPromptReport (e.g. allowlistMismatches: ["nonexistent_tool"]) so it appears in the JSON run output, not only on stderr. This makes the issue trivially visible in CI, gateway-fronted runs, and observability gists where stderr may be invisible.
Suggested location
The warning string These entries won't match any tool unless the plugin is enabled appears in the bundled dist/openclaw-tools-CHy2rPF4.js. The intersection-computing function there is the right place to add a fail-fast policy gate and a structured-mismatch field in the run report.
Summary
When an agent's
tools.allowallowlist is non-empty but every name in it fails to resolve to a registered tool (the named tools are not built-in and not registered by any loaded plugin), the runtime emits a one-time stderr warning and then proceeds to run the agent with an empty function-call schema (systemPromptReport.tools.entries=[],schemaChars=0). The agent has no callable tools, but the model's output is unconstrained, so it tends to produce plausible-looking JSON or "tool result" text that downstream consumers cannot distinguish from real tool invocations. There is no tool-call span emitted, no provider-side function call recorded, and the run completes withstopReason: "stop"as if everything succeeded.This is a quiet integrity failure: the agent claims to have done work it could not have done, and the only fail-loud signal is a stderr warning that's easy to miss in CI logs or Docker stdouts.
Severity
P1 — not a crash, but a category of silent-wrong-output that's hard for users to detect. I encountered it via a misconfigured sample MAS (sister bug: outshift-open/insightClaw#34):
tools.allow=["query_db"]referenced a name nothing registers. The agent fabricatedquery_dbresponses for two consecutive turns without ever calling a tool, and the output looked correct enough that I had to cross-check the underlying HTTP service's access log to detect the hallucination.Reproduction
tools.allow: ["nonexistent_tool"]inopenclaw.json(no plugin defines it).openclaw agent --agent <id> --json -m "Use nonexistent_tool to fetch data".Observed:
[tools] agents.<id>.tools.allow allowlist contains unknown entries (nonexistent_tool). These entries won't match any tool unless the plugin is enabled."tools": {"listChars": 0, "schemaChars": 0, "entries": []}.toolCallevents.I have a concrete real-world repro: two probes against a
dbagent whosetools.allow=["query_db"], both produced fabricated JSON that did not match the underlying HTTP service's actual response shape, and the service access log shows zero traffic from those probes.Expected
Some fail-louder behavior. Options, roughly in order of preference:
agent <id> has no callable tools after allowlist filtering. Safest default.tools.allowresolution mismatch as a structured field insystemPromptReport(e.g.allowlistMismatches: ["nonexistent_tool"]) so it appears in the JSON run output, not only on stderr. This makes the issue trivially visible in CI, gateway-fronted runs, and observability gists where stderr may be invisible.Suggested location
The warning string
These entries won't match any tool unless the plugin is enabledappears in the bundleddist/openclaw-tools-CHy2rPF4.js. The intersection-computing function there is the right place to add a fail-fast policy gate and a structured-mismatch field in the run report.