fix(message-tool): rename type schema property to avoid JSON Schema keyword collision#78920
Conversation
|
Codex review: needs maintainer review before merge. Summary Reproducibility: yes. The PR body gives a concrete live NVIDIA NIM replay path with before/after terminal output, and current-main source still exposes Real behavior proof Next step before merge Security Review detailsBest possible solution: Land the narrow schema rename with the Discord parser fallback and focused regression coverage so provider-safe tool schemas and Discord channel-create compatibility are both preserved. Do we have a high-confidence way to reproduce the issue? Yes. The PR body gives a concrete live NVIDIA NIM replay path with before/after terminal output, and current-main source still exposes Is this the best way to solve the issue? Yes. Renaming the model-exposed field to Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against f74436dc81bc. |
20b63e5 to
dbc0231
Compare
dbc0231 to
ea45866
Compare
The `message` tool exposed a parameter literally named `type` (Discord's numeric channel type). Some OpenAI-compatible providers — notably NVIDIA NIM with moonshotai/kimi-k2.6 — choke on a JSON-Schema property whose name collides with the JSON-Schema `type` keyword, returning HTTP 500 `Internal server error: unhashable type: 'dict'` whenever the tool is included. Renaming the schema property to `channelType` eliminates the collision. The Discord runtime keeps a fallback that reads `channelType` first and falls back to the legacy `type` so any in-flight tool calls still work. Repro before fix: - POST /v1/chat/completions to integrate.api.nvidia.com with the message tool included → HTTP 500 - Same payload with `type` renamed to `channelType` → HTTP 200 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
ea45866 to
669084a
Compare
|
Merged via squash.
Thanks @YashSaliya! |
… keyword collision (openclaw#78920) Merged via squash. Prepared head SHA: 669084a Co-authored-by: YashSaliya <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
… keyword collision (openclaw#78920) Merged via squash. Prepared head SHA: 669084a Co-authored-by: YashSaliya <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
… keyword collision (openclaw#78920) Merged via squash. Prepared head SHA: 669084a Co-authored-by: YashSaliya <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
… keyword collision (openclaw#78920) Merged via squash. Prepared head SHA: 669084a Co-authored-by: YashSaliya <[email protected]> Co-authored-by: altaywtf <[email protected]> Reviewed-by: @altaywtf
Summary
The
messagetool's parameter schema (built insrc/agents/tools/message-tool.ts'sbuildChannelManagementSchema) defines a property literally namedtype(Discord's numeric channel type). Property names that collide with JSON-Schema keywords break some OpenAI-compatible providers — confirmed against NVIDIA NIM (https://integrate.api.nvidia.com/v1/chat/completions) withmoonshotai/kimi-k2.6, which returnsHTTP 500 Internal server error: unhashable type: 'dict'for any chat-completions request that includes themessagetool. RenamingtypetochannelTyperesolves the collision.Change
src/agents/tools/message-tool.ts: renametype→channelTypeinbuildChannelManagementSchema, with a description noting why.extensions/discord/src/actions/runtime.shared.ts:readDiscordChannelCreateParamsnow readschannelTypefirst, falling back totypefor backward compatibility with any in-flight tool calls.Why this fix
NVIDIA NIM's parser is technically buggy —
{"type": {"type": "number"}}is valid JSON Schema. But colliding property names with reserved JSON-Schema keywords is a fragile pattern that has now been observed to break a real, popular provider, and the rename costs nothing semantically (the LLM only sees the field name; Discord's actual channel-creation payload is constructed downstream).Real behavior proof
Behavior or issue addressed: Any OpenClaw chat-completions call to NVIDIA NIM (
integrate.api.nvidia.com) with themessagetool present fails withHTTP 500 Internal server error: unhashable type: 'dict', surfaced through OpenClaw's failover layer as a permanentmodel_not_foundfailure. Bisecting the captured request body identifies the property literally namedtypeinside themessagetool's JSON-Schema as the sole trigger. After the fix, the same call succeeds with HTTP 200 and a real model completion.Real environment tested: Live NVIDIA NIM endpoint
https://integrate.api.nvidia.com/v1/chat/completionswith a realnvapi-…API key (redacted) and real modelmoonshotai/kimi-k2.6. OpenClaw 2026.4.14 (323493f) installed vianpm i -g openclaw, running locally on macOS 24.1.0 with Node 22. Provider config:{ baseUrl: "https://integrate.api.nvidia.com/v1", api: "openai-completions", apiKey: "nvapi-…" }. Captured the outbound bodies via a local HTTP capture proxy (http://127.0.0.1:8765/v1) configured as the NVIDIA NIMbaseUrl, then replayed against the live NVIDIA endpoint to isolate the field.Exact steps or command run after this patch:
/usr/local/lib/node_modules/openclaw/dist/openclaw-tools-*.jsandruntime-*.js) — same change as in this PR's diff.openclaw gateway stop && openclaw gateway.type→channelTypeapplied:Evidence after fix:
Live terminal output from step 3 — replaying the previously-failing OpenClaw body, with only
typerenamed tochannelType, against the production NVIDIA NIM endpoint:Live console output from step 4 — patched OpenClaw, real NVIDIA NIM call, real completion streamed back:
For comparison, the same
openclaw infer model runagainst the unpatched bundle produced (verbatim):Field-level bisection of the captured OpenClaw body, replayed against live NVIDIA, isolated the offender:
Observed result after fix: Curl replay of the previously-failing captured OpenClaw payload (with only
type→channelTypeapplied) returnsHTTP 200and a validchat.completionJSON object — copied stdout above.openclaw infer model run --local --model nvidia-nim/moonshotai/kimi-k2.6 --prompt "ok"returns a streamed assistant reply through the patched OpenClaw bundle — copied stdout above. Pre-patch, the same command consistently terminates withHTTP 500: Internal server error: unhashable type: 'dict'after exhausting failover.What was not tested: Other OpenAI-compatible providers were not regression-tested in this manual pass; the change only renames a property name and preserves backward-compatible reading via the legacy
typefallback inreadDiscordChannelCreateParams, so no behavior change is expected for OpenAI / Anthropic / Groq / Ollama / etc. Discord live channel-create against a real Discord guild was not exercised in this run — only the parameter reader was changed (with fallback to legacytype), so existing Discord flows continue to work.