fix(validation): preserve null in anyOf unions instead of coercing to empty string (fixes #96716)#97039
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed June 26, 2026, 10:43 AM ET / 14:43 UTC. Summary PR surface: Source +15, Tests +25. Total +40 across 2 files. Reproducibility: yes. source-reproducible: current main validates tool arguments before execution and can coerce Review metrics: 2 noteworthy metrics.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land one canonical fix that preserves schema-declared nullable nulls on the real OpenClaw MCP path, backed by focused regression coverage and redacted configured MCP proof. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible: current main validates tool arguments before execution and can coerce Is this the best way to solve the issue? Yes for the narrowed validation bug: preserving schema-declared null in the shared validation layer is cleaner than broad MCP null stripping. It is not merge-ready until maintainers accept the compatibility semantics, pick the canonical sibling path, and the contributor adds real MCP proof. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 1cd6f81a46ae. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +15, Tests +25. Total +40 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
… empty string Fixes openclaw#96716
35af6af to
c3b2539
Compare
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review |
What Problem This Solves
Fixes #96716.
MCP tool calls with optional arguments set to JSON null fail via OpenClaw but succeed via other MCP clients. Root cause: when a JSON schema uses
anyOf [{type: string}, {type: null}], the union coercion (coerceWithUnionSchema) tries the string branch first, which coercesnullto""(a valid string), and returns that before ever trying the{type: null}branch. The MCP server receives""instead ofnull, triggering a different code path.Why This Change Was Made
coerceWithUnionSchemaiterated schemas in order and appliedcoerceWithJsonSchemabefore validating. ForanyOf [{type: string}, {type: null}]with valuenull, the first schema coercesnull → ""and the validator passes — so the null branch is never tried.The fix is narrowly scoped: when value is
null, check if any union member accepts null directly (type: "null") before falling through to coercion. This only adds behavior for thenullcase, without affecting other union types likenumber | string.Changes Made
packages/llm-core/src/validation.ts: Added null-first check incoerceWithUnionSchema.packages/llm-core/src/validation.test.ts: Added regression test.Evidence
node scripts/run-vitest.mjs run packages/llm-core/src/validation.test.ts— 3/3 passedReal behavior proof
Schema:
insight_id: anyOf [{type: string}, {type: null}](from issue #96716)Before fix (simulated): null would be coerced to
""via the string branch, causing the MCP server to error.After fix: null matches
{type: null}directly and is preserved.anyOf [string, null]schemas preserved as nullWhat was not tested: End-to-end with a real remote MCP server (the fix is in the shared validation layer — all tool calls benefit from it).