Skip to content

fix(message-tool): rename type schema property to avoid JSON Schema keyword collision#78920

Merged
altaywtf merged 3 commits into
openclaw:mainfrom
YashSaliya:fix/message-tool-type-collision
May 15, 2026
Merged

fix(message-tool): rename type schema property to avoid JSON Schema keyword collision#78920
altaywtf merged 3 commits into
openclaw:mainfrom
YashSaliya:fix/message-tool-type-collision

Conversation

@YashSaliya

@YashSaliya YashSaliya commented May 7, 2026

Copy link
Copy Markdown
Contributor

Summary

The message tool's parameter schema (built in src/agents/tools/message-tool.ts's buildChannelManagementSchema) defines a property literally named type (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) with moonshotai/kimi-k2.6, which returns HTTP 500 Internal server error: unhashable type: 'dict' for any chat-completions request that includes the message tool. Renaming type to channelType resolves the collision.

Change

  • src/agents/tools/message-tool.ts: rename typechannelType in buildChannelManagementSchema, with a description noting why.
  • extensions/discord/src/actions/runtime.shared.ts: readDiscordChannelCreateParams now reads channelType first, falling back to type for 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 the message tool present fails with HTTP 500 Internal server error: unhashable type: 'dict', surfaced through OpenClaw's failover layer as a permanent model_not_found failure. Bisecting the captured request body identifies the property literally named type inside the message tool'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/completions with a real nvapi-… API key (redacted) and real model moonshotai/kimi-k2.6. OpenClaw 2026.4.14 (323493f) installed via npm 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 NIM baseUrl, then replayed against the live NVIDIA endpoint to isolate the field.

Exact steps or command run after this patch:

  1. Apply the rename to the installed dist files (/usr/local/lib/node_modules/openclaw/dist/openclaw-tools-*.js and runtime-*.js) — same change as in this PR's diff.
  2. Restart the gateway: openclaw gateway stop && openclaw gateway.
  3. Replay the captured OpenClaw payload directly against NVIDIA, with only typechannelType applied:
    curl -sS https://integrate.api.nvidia.com/v1/chat/completions \
      -H "Authorization: Bearer $NVAPI_KEY" \
      -H "Content-Type: application/json" \
      --data-binary @fixed.json -w "HTTP %{http_code}\n"
    
  4. Run a one-shot turn through OpenClaw end-to-end:
    openclaw infer model run --local \
      --model "nvidia-nim/moonshotai/kimi-k2.6" --prompt "ok"
    

Evidence after fix:

Live terminal output from step 3 — replaying the previously-failing OpenClaw body, with only type renamed to channelType, against the production NVIDIA NIM endpoint:

$ curl -sS -o /tmp/r.out -w "HTTP %{http_code}\n" \
    https://integrate.api.nvidia.com/v1/chat/completions \
    -H "Authorization: Bearer $NVAPI_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @fixed.json
HTTP 200

$ head -c 240 /tmp/r.out
{"id":"2ebc71e87084441b80c4ba7d137fd6bf","object":"chat.completion",
 "created":1778154280,"model":"moonshotai/kimi-k2.6",
 "choices":[{"index":0,"message":{"role":"assistant","content":"Hey there! 👋 W…

Live console output from step 4 — patched OpenClaw, real NVIDIA NIM call, real completion streamed back:

$ openclaw infer model run --local \
    --model "nvidia-nim/moonshotai/kimi-k2.6" --prompt "ok"
model.run via local
provider: nvidia-nim
model: moonshotai/kimi-k2.6
outputs: 1
The user said "ok", which is a very brief acknowledgment...
Hey. Just came online. Who am I? And who are you?
I'm fresh out the box here — no memories, no name, no idea who I'm talking to…

For comparison, the same openclaw infer model run against the unpatched bundle produced (verbatim):

[diagnostic] lane task error: lane=main durationMs=586 error="HTTP 500: Internal server error: unhashable type: 'dict'"
[model-fallback/decision] requested=nvidia-nim/moonshotai/kimi-k2.6 reason=model_not_found
HTTP 500: Internal server error: unhashable type: 'dict'

Field-level bisection of the captured OpenClaw body, replayed against live NVIDIA, isolated the offender:

[200] tools[0:6]                           ok
[500] tools[6:12]                          fail
[200] tools[7:8]    (cron)                 ok
[500] tools[8:9]    (message)              fail   ← single tool reproduces it

within tools[8].function.parameters.properties:
  without 'name'                : FAIL
  without 'type'                : ok       ← removing only `type` clears it
  without 'parentId'            : FAIL
  ... (all other properties)    : FAIL

Observed result after fix: Curl replay of the previously-failing captured OpenClaw payload (with only typechannelType applied) returns HTTP 200 and a valid chat.completion JSON 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 with HTTP 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 type fallback in readDiscordChannelCreateParams, 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 legacy type), so existing Discord flows continue to work.

@openclaw-barnacle openclaw-barnacle Bot added channel: discord Channel integration: discord agents Agent runtime and tooling size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 7, 2026
@clawsweeper

clawsweeper Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

Summary
The PR renames the message tool's Discord channel-create schema field from type to channelType, adds a legacy parser fallback and regression test, and notes the fix in the changelog.

Reproducibility: yes. The PR body gives a concrete live NVIDIA NIM replay path with before/after terminal output, and current-main source still exposes type while the schema normalization path does not rename property keys.

Real behavior proof
Sufficient (terminal): The PR body contains redacted terminal proof from a live NVIDIA NIM replay and a patched OpenClaw run showing after-fix HTTP 200 and a real completion.

Next step before merge
No ClawSweeper repair lane is needed because the PR already contains the focused code, test, and changelog changes; the remaining work is maintainer validation, checks, and a merge decision.

Security
Cleared: The diff only changes local tool schema, Discord parameter parsing, a focused test, and changelog text; it does not touch CI, dependencies, secrets, publishing, or external code execution.

Review details

Best 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 type while the schema normalization path does not rename property keys.

Is this the best way to solve the issue?

Yes. Renaming the model-exposed field to channelType while preserving legacy type parsing in Discord is narrower and less provider-specific than adding an NVIDIA-only schema rewrite.

Acceptance criteria:

  • node scripts/run-vitest.mjs extensions/discord/src/actions/runtime.test.ts
  • normal PR checks/CI for the exact head SHA before merge

What I checked:

  • Current main still exposes type in the message tool schema: buildChannelManagementSchema defines the model-facing channel-management parameter as type: Type.Optional(Type.Number()), so the schema-key collision described by the PR is still present on main. (src/agents/tools/message-tool.ts:470, f74436dc81bc)
  • Current Discord runtime reads the legacy field: readDiscordChannelCreateParams reads Discord channel-create type from tool parameters, which is why the PR needs a channelType-first fallback rather than only renaming the schema. (extensions/discord/src/actions/runtime.shared.ts:49, f74436dc81bc)
  • Channel-create runtime uses the shared parser: The Discord guild action passes readDiscordChannelCreateParams(params) into createChannelDiscord, confirming the parser is on the affected runtime path. (extensions/discord/src/actions/runtime.guild.ts:326, f74436dc81bc)
  • Schema normalization keeps property names: The provider schema normalization path cleans and merges object properties by their existing keys; it does not rename a property key such as type before provider submission. (src/agents/pi-tools-parameter-schema.ts:251, f74436dc81bc)
  • PR diff is focused and includes compatibility coverage: The live PR diff changes only the schema field, Discord parameter reader, a focused runtime test preferring channelType over legacy type, and a changelog line. (extensions/discord/src/actions/runtime.test.ts:910, 669084aad81c)
  • Real behavior proof supplied in PR body: The PR body includes redacted live NVIDIA NIM curl replay output showing HTTP 200 after renaming only type to channelType, plus a patched OpenClaw run with a real completion and the pre-patch HTTP 500 output. (669084aad81c)

Likely related people:

  • NicholasSpisak: The affected message-tool channel-management schema and underlying Discord channel/category management feature trace to commits authored as Nicholas Spisak / NickSpisak_. (role: original feature contributor signal; confidence: high; commits: d63eae528c08, cfcff68e91f1; files: src/agents/tools/message-tool.ts, CHANGELOG.md)
  • steipete: Shortlog over the affected message-tool, Discord runtime, and changelog paths shows Peter Steinberger as the dominant contributor in this area, making him a useful routing contact for final review. (role: heavy adjacent area contributor; confidence: medium; files: src/agents/tools/message-tool.ts, extensions/discord/src/actions/runtime.shared.ts, extensions/discord/src/actions/runtime.guild.ts)
  • altaywtf: The live PR is assigned to altaywtf, and the latest PR commits for the test refresh and changelog update are authored/committed by altaywtf. (role: likely PR follow-up owner; confidence: medium; commits: e596a50ae1c1, 669084aad81c; files: extensions/discord/src/actions/runtime.test.ts, CHANGELOG.md)

Remaining risk / open question:

  • I did not execute tests in this read-only review; the exact PR head should still pass the focused Discord runtime test and normal PR checks before merge.
  • The supplied live proof covers NVIDIA NIM and patched OpenClaw, while Discord channel-create compatibility is covered by the parser fallback and focused unit test rather than a live guild run.

Codex review notes: model gpt-5.5, reasoning high; reviewed against f74436dc81bc.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 7, 2026
@YashSaliya
YashSaliya marked this pull request as ready for review May 7, 2026 12:56
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 7, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 7, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 7, 2026
@altaywtf altaywtf self-assigned this May 15, 2026
@altaywtf
altaywtf force-pushed the fix/message-tool-type-collision branch from 20b63e5 to dbc0231 Compare May 15, 2026 11:45
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@altaywtf
altaywtf force-pushed the fix/message-tool-type-collision branch from dbc0231 to ea45866 Compare May 15, 2026 12:37
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
Manish Gupta and others added 3 commits May 15, 2026 16:19
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]>
@altaywtf
altaywtf force-pushed the fix/message-tool-type-collision branch from ea45866 to 669084a Compare May 15, 2026 13:19
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 15, 2026
@altaywtf
altaywtf merged commit d91f58e into openclaw:main May 15, 2026
118 of 119 checks passed
@altaywtf

Copy link
Copy Markdown
Member

Merged via squash.

Thanks @YashSaliya!

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
… 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
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
… 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
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
… 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
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
… 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling channel: discord Channel integration: discord proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants