openclaw - 💡(How to fix) Fix Duplicate `tool_call.id` (`exec:N`) sent to Moonshot causes deterministic 400 on multi-tool turns

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

When the assistant emits two or more tool calls in a single turn and the active provider is Moonshot (moonshot/kimi-k2.6), OpenClaw constructs the outgoing request with duplicate tool_call.id values (e.g. two exec:2 entries). Moonshot's OpenAI-compat endpoint rejects the payload with a 400, and the run falls over to the configured fallback model.

The bug is deterministic — the same error hash repeats across every multi-tool turn that targets Kimi. xAI/Grok happens to accept duplicate IDs, so the fallback succeeds and the user-visible response looks fine, but every multi-tool turn pays an extra Kimi request + a Grok retry.

Error Message

The bug is deterministic — the same error hash repeats across every multi-tool turn that targets Kimi. xAI/Grok happens to accept duplicate IDs, so the fallback succeeds and the user-visible response looks fine, but every multi-tool turn pays an extra Kimi request + a Grok retry.

Observed error (from gateway log)

Root Cause

Root cause (suspected)

Fix Action

Fix / Workaround

Workarounds tried

Happy to provide raw log lines, payload captures (after redacting the API key), or test against a patch.

Code Example

openclaw agent \
  --agent main \
  --once \
  --prompt "Busca el precio actual de XAU/USD y también el de BTC/USD, y muéstrame ambos."

---

400 Invalid request: tool call id exec:2 is duplicated

---

{
  "event": "model_fallback_decision",
  "decision": "candidate_failed",
  "requestedProvider": "moonshot",
  "requestedModel": "kimi-k2.6",
  "candidateProvider": "moonshot",
  "candidateModel": "kimi-k2.6",
  "reason": "format",
  "status": 400,
  "errorPreview": "400 Invalid request: tool call id exec:2 is duplicated",
  "errorHash": "sha256:544ca2415216",
  "providerRuntimeFailureKind": "schema",
  "fallbackStepFromModel": "moonshot/kimi-k2.6",
  "fallbackStepToModel": "xai/grok-4.3"
}
RAW_BUFFERClick to expand / collapse

Duplicate tool_call.id (exec:N) sent to Moonshot causes deterministic 400 on multi-tool turns

Summary

When the assistant emits two or more tool calls in a single turn and the active provider is Moonshot (moonshot/kimi-k2.6), OpenClaw constructs the outgoing request with duplicate tool_call.id values (e.g. two exec:2 entries). Moonshot's OpenAI-compat endpoint rejects the payload with a 400, and the run falls over to the configured fallback model.

The bug is deterministic — the same error hash repeats across every multi-tool turn that targets Kimi. xAI/Grok happens to accept duplicate IDs, so the fallback succeeds and the user-visible response looks fine, but every multi-tool turn pays an extra Kimi request + a Grok retry.

Environment

  • OpenClaw: 2026.5.18 (commit 50a2481)
  • Node: v22.22.2
  • OS: Linux 6.8.0-111-generic (Ubuntu)
  • Primary model: moonshot/kimi-k2.6
  • Fallback model: xai/grok-4.3
  • Search provider: kimi (tools.web.search.provider)
  • Other relevant config: defaults; no compat overrides on Moonshot models.

Reproduction

Any prompt that triggers more than one tool call in a single assistant turn against Kimi will hit this. Minimal repro via the CLI:

openclaw agent \
  --agent main \
  --once \
  --prompt "Busca el precio actual de XAU/USD y también el de BTC/USD, y muéstrame ambos."

This forces the model to plan two web_search calls. On Kimi, the request to Moonshot's /v1/chat/completions is rejected:

400 Invalid request: tool call id exec:2 is duplicated

Observed error (from gateway log)

{
  "event": "model_fallback_decision",
  "decision": "candidate_failed",
  "requestedProvider": "moonshot",
  "requestedModel": "kimi-k2.6",
  "candidateProvider": "moonshot",
  "candidateModel": "kimi-k2.6",
  "reason": "format",
  "status": 400,
  "errorPreview": "400 Invalid request: tool call id exec:2 is duplicated",
  "errorHash": "sha256:544ca2415216",
  "providerRuntimeFailureKind": "schema",
  "fallbackStepFromModel": "moonshot/kimi-k2.6",
  "fallbackStepToModel": "xai/grok-4.3"
}

Observed across 3 distinct runIds (30f2eded-…, b84182e6-…, b133432e-…) and 44 total log lines today, all with the same errorHash — confirming it's deterministic, not a race.

Expected behavior

OpenAI-style tool-calling requires that each tool_call.id in a single assistant message be unique. Moonshot enforces this; xAI happens to not. OpenClaw should generate unique IDs regardless of provider.

Root cause (suspected)

tool_call.id is built as exec:${approvalId} (see bash-tools-BOznQu4J.js:420, acp-cli-CgTmNdZv.js:1134). When the assistant emits multiple tool calls in the same turn, the approvalId counter is not advancing per-call — multiple calls in one turn share the same approval scope and therefore the same id. The collision is invisible to xAI but fatal to Moonshot.

Suggested fix

Make tool_call.id unique within a single assistant message by either:

  1. Including the call's index in the turn: exec:${approvalId}:${callIndex}, or
  2. Salting with a short random suffix per call: exec:${approvalId}:${shortHash}.

Option (1) is preferable — deterministic, debuggable, and traceable back to the approval. The mapping back to approvalId for display/UI can still strip the suffix.

Workarounds tried

  • compat.nativeWebSearchTool: true on the Moonshot model — surfaced a different schema rejection (no Moonshot compat preset exists analogous to xAI's, so the payload was missing required transformations). Reverted.
  • Switching tools.web.search.provider to kimi — does not help; the duplicate-ID failure is upstream of which search backend serves the call.
  • Relying on fallback — works (Grok takes over), but every multi-tool turn pays double latency and double tokens.

Impact

Anyone running Kimi as the primary model with tools enabled hits this on every multi-tool turn. The user experience is "slow but correct" (because the Grok fallback covers it), which masks the bug — the gateway logs are the only signal that something is wrong.

Additional context

Happy to provide raw log lines, payload captures (after redacting the API key), or test against a patch.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

OpenAI-style tool-calling requires that each tool_call.id in a single assistant message be unique. Moonshot enforces this; xAI happens to not. OpenClaw should generate unique IDs regardless of provider.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Duplicate `tool_call.id` (`exec:N`) sent to Moonshot causes deterministic 400 on multi-tool turns