Skip to content

[Bug]: Plugin TypeBox tool schemas rejected as 'missing parameters object' by describeMalformedPluginTool — memory tools silently dropped every turn #75244

Description

@mikronn2

Bug type

Behavior bug (incorrect output/state without crash)

Summary

Plugin tools that use TypeBox Type.Object() schemas for their parameters field are rejected by OpenClaw's describeMalformedPluginTool validator with "missing parameters object" on every agent turn. The tools are silently dropped, the error is logged but never surfaced to the user, and the agent loses access to those tools without any indication.

In our case, the openclaw-remnic memory plugin's memory_search and memory_get tools are rejected every turn, causing:

  • Cascading context overflow (agent compensates with more tool calls without memory recall)
  • Stuck sessions (processing state for 5+ minutes)
  • Auto-compaction loops (93 messages, 171KB session, compaction takes 3.5 minutes)
  • Agent appears to "just stop responding" from the user's perspective

Observed in the wild

Plugin: openclaw-remnic v9.3.82
OpenClaw: 2026.4.27
Model: ollama/glm-5.1:cloud

The plugin registers memory_search and memory_get via buildMemorySearchTool() and buildMemoryGetTool(). Both return objects with parameters: Type2.Object({...}) (TypeBox @sinclair/[email protected]).

Every agent turn produces:

plugin tool is malformed (openclaw-remnic): memory_search missing parameters object
plugin tool is malformed (openclaw-remnic): memory_get missing parameters object

Other tools from the same plugin (e.g., continuity_audit_generate, store_episode) that also use TypeBox Type.Object() inline in their registration work fine. The difference: the memory_search/memory_get tools store their schema in a module-level var (MemorySearchInputSchema, MemoryGetInputSchema) and reference it by name.

Root cause analysis

The validation in tools-Dt0Yx7_E.js:

function isRecord(value) {
    return Boolean(value && typeof value === "object" && !Array.isArray(value));
}

function describeMalformedPluginTool(tool) {
    if (!isRecord(tool)) return "tool must be an object";
    const name = readPluginToolName(tool);
    if (!name) return "missing non-empty name";
    if (typeof tool.execute !== "function") return `${name} missing execute function`;
    if (!isRecord(tool.parameters)) return `${name} missing parameters object`;
}

The tool object returned by buildMemorySearchTool:

{
    name: "memory_search",
    description: "Search Remnic memories for the OpenClaw active-memory surface.",
    parameters: MemorySearchInputSchema,  // TypeBox Type2.Object({query: Type2.String(), ...})
    execute: async function(...) { ... }
}

A TypeBox Type.Object() produces {type: "object", required: [...], properties: {...}} — a valid plain JS object that should pass isRecord(). Yet isRecord(tool.parameters) returns false at runtime.

Suspected cause: TypeBox version mismatch. The plugin ships @sinclair/[email protected] while OpenClaw bundles [email protected] (a different package entirely). When jiti loads the plugin in-process, there may be a resolution conflict where Type2.Object() produces a schema object that does not satisfy isRecord at validation time — possibly due to Symbol-based internal properties, class instances, or Proxies in the v1.x schema format.

The fact that other TypeBox-registered tools from the same plugin work (they use Type.Object() inline at the call site rather than referencing a module-level var) adds weight to a jiti/hoisting/resolution timing theory, though we could not definitively confirm this without adding debug logging to describeMalformedPluginTool.

Impact

  • Silent tool loss: The agent loses critical tools with no user-visible error
  • Cascading failures: Without memory tools, agents compensate with more tool calls → context overflow → stuck sessions → compaction loops
  • Recurring: The error fires on EVERY agent turn (tools are re-registered and re-validated each turn), producing 2+ error log entries per turn
  • Not model-specific: This is a registration-time bug, not adapter-specific (unlike the closed [Bug]: A single broken plugin's malformed tool schema crashes every Anthropic agent run #69423 which was Anthropic-specific)

Steps to reproduce

  1. Install openclaw-remnic plugin (v9.3.82+)
  2. Configure OpenClaw with plugins.slots.memory = "openclaw-remnic" and openclaw-remnic.config.openclawToolsEnabled !== false
  3. Start an agent session
  4. Observe logs: plugin tool is malformed (openclaw-remnic): memory_search missing parameters object and same for memory_get
  5. Agent cannot use memory tools — no memory_search or memory_get available

Proposed fix (ranked by impact/effort)

1. Improve diagnostics in describeMalformedPluginTool (cheap, high value)

Log the actual value and type of tool.parameters when it fails isRecord, so developers can debug TypeBox/Schema version issues:

if (!isRecord(tool.parameters)) {
    return `${name} missing parameters object (got ${typeof tool.parameters}: ${JSON.stringify(tool.parameters)?.slice(0, 200)})`;
}

2. Accept both parameters and inputSchema (medium, high value)

Some plugin schemas use the MCP convention inputSchema instead of parameters:

const schema = tool.parameters ?? tool.inputSchema;
if (!isRecord(schema)) return `${name} missing parameters object`;

This would align with the fix pattern from #69423.

3. Add TypeBox-aware validation (medium, medium value)

TypeBox schemas carry a [Symbol.for("@sinclair/typebox/Kind")] property. If tool.parameters has this symbol, treat it as valid even if other checks fail:

function isValidToolSchema(value) {
    if (isRecord(value)) return true;
    if (value && typeof value === "object" && value[Symbol.for("@sinclair/typebox/Kind")]) return true;
    return false;
}

4. Document the parameters contract for plugin developers (cheap, medium value)

The plugin SDK docs should explicitly state that parameters must be a JSON Schema object (plain record), not a TypeBox class instance or other schema builder output that does not serialize to a plain object.

Environment

  • OpenClaw version: 2026.4.27
  • Plugin: openclaw-remnic v9.3.82 (TypeBox @sinclair/[email protected])
  • OS: Ubuntu Linux (aidevbot-cass)
  • Node.js: v24.14.0
  • Model: ollama/glm-5.1:cloud

Related issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions