Skip to content

[Bug]: Agent runs silently with empty function-call schema when tools.allow contains only undefined tool names — model hallucinates tool calls and results #71292

Description

@mayank6136

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

  1. openclaw 2026.4.12 (1c0672b).
  2. Configure any agent with tools.allow: ["nonexistent_tool"] in openclaw.json (no plugin defines it).
  3. 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:

  1. 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.
  2. 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.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions