Skip to content

Instantly share code, notes, and snippets.

@Logopher
Created May 17, 2026 14:29
Show Gist options
  • Select an option

  • Save Logopher/45b30a6df64f5782e1084474446bcd9b to your computer and use it in GitHub Desktop.

Select an option

Save Logopher/45b30a6df64f5782e1084474446bcd9b to your computer and use it in GitHub Desktop.
## Root Cause Analysis: Schema Mismatch in memory_get / memory_search Tool Registration

### Summary

The TypeBox schema rejection reported in #75244 is symptomatic of a broader architectural mismatch in the `memory-core` plugin. The root cause is **duplicate schema definitions** that use incompatible approaches, causing tool validation to fail silently and tools to be dropped from the agent's tool registry.

### The Problem

**Two conflicting schema definitions exist:**

1. **In `extensions/memory-core/index.ts` (lines 61-83):**
   - Uses plain JSON Schema objects (`type: "object"`, `properties: {}`, `required: []`)
   - Directly instantiated as static module-level constants
   - Example: `const MemorySearchSchema = { type: "object", properties: {...}, required: ["query"], ...}`

2. **In `extensions/memory-core/src/tools.shared.ts` (lines 34-44):**
   - Uses TypeBox `Type.Object()` API (`@sinclair/typebox`)
   - Produces TypeBox-wrapped schema objects with internal Symbol properties
   - Example: `export const MemorySearchSchema = Type.Object({ query: Type.String(), ... })`

### What Happens

1. **Lazy loader** (`index.ts`) passes the plain JSON schema to the tool factory
2. **Actual tool implementation** (`tools.ts`) imports and uses the TypeBox schema from `tools.shared.ts`
3. **Tool validation** expects `parameters` to be a plain record object (`isRecord()` check)
4. **TypeBox schema object** fails `isRecord()` due to internal Symbol properties or Proxy wrapping
5. **Tool is rejected** with `"memory_search missing parameters object"` error
6. **Tools are silently dropped** from the agent's runtime tool registry (error logged but never surfaced)

### Evidence

**File: `extensions/memory-core/index.ts` (lazy wrapper layer)**
```typescript
const MemoryGetSchema = {
  type: "object",
  properties: {
    path: { type: "string" },
    from: { type: "number" },
    lines: { type: "number" },
    corpus: { type: "string", enum: ["memory", "wiki", "all"] },
  },
  required: ["path"],
  additionalProperties: false,
} as const satisfies TSchema;

File: extensions/memory-core/src/tools.shared.ts (actual tool implementation)

export const MemoryGetSchema = Type.Object({
  path: Type.String(),
  from: Type.Optional(Type.Number()),
  lines: Type.Optional(Type.Number()),
  corpus: Type.Optional(stringEnum(["memory", "wiki", "all"])),
});

File: tools-Dt0Yx7_E.js (validation that rejects the tool)

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

// This fails for TypeBox schemas:
if (!isRecord(tool.parameters)) return `${name} missing parameters object`;

Impact

  • Silent tool loss: Agents lose memory_search and memory_get without visible error
  • Cascading failures: Without memory tools, agents compensate with more tool calls → context overflow → stuck sessions
  • Recurring per-turn: Error fires on every agent turn (tools are re-validated each turn)
  • Workaround required: External plugins like openclaw-remnic forced to inline schemas instead of referencing module-level variables

Solution Options (Priority Order)

  1. Unify schemas to plain JSON (Recommended):

    • Remove TypeBox from tools.shared.ts
    • Use plain JSON Schema objects everywhere
    • Lowest risk, maintains current validation logic
    • Effort: Low
  2. Update validation to accept TypeBox:

    • Add TypeBox-aware check: value?.[Symbol.for("@sinclair/typebox/Kind")]
    • Or use a try-catch around isRecord() with schema serialization fallback
    • Effort: Low-Medium
  3. Centralize schema definitions:

    • Consolidate schemas to a single location
    • Use a schema builder that produces plain JSON objects
    • Effort: Medium

Recommendation

Implement Option 1 (unify to plain JSON) in tools.shared.ts:

  • Remove TypeBox dependency from schema definitions
  • Keep TypeBox only where needed for other schema transformations
  • Add a regression test: "Tool with module-level schema reference should not be rejected as malformed"

This aligns with the fix pattern from #69423 (tool parameter validation) and will resolve the cascading failures experienced by users of both bundled and third-party memory plugins.


---

**Note for the issue:** This analysis should be cross-linked with:
- #64877 (config.patch schema validation issues)
- #72885 (memory tool corpus field mislabeling)
- #83013 (tencentdb config schema rejection)

All point to the same root cause: schema definition/validation mismatch in tool registration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment