openclaw - ✅(Solved) Fix [Bug]: openai-completions: Kimi/SGLang streamed tool-call arguments can arrive in a shape that breaks tool dispatch [1 pull requests, 1 comments, 1 participants]

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…
GitHub stats
openclaw/openclaw#69672Fetched 2026-04-22 07:49:30
View on GitHub
Comments
1
Participants
1
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
labeled ×2commented ×1

When using a self-hosted Kimi model behind an OpenAI-compatible SGLang endpoint with OpenClaw’s openai-completions transport, tool-call conversations can break because streamed tool-call arguments are not always reconstructed into usable tool args before dispatch.

In my setup, this surfaced as tools being invoked with empty or malformed arguments, which then broke the turn.

Root Cause

When using a self-hosted Kimi model behind an OpenAI-compatible SGLang endpoint with OpenClaw’s openai-completions transport, tool-call conversations can break because streamed tool-call arguments are not always reconstructed into usable tool args before dispatch.

Fix Action

Fix / Workaround

When using a self-hosted Kimi model behind an OpenAI-compatible SGLang endpoint with OpenClaw’s openai-completions transport, tool-call conversations can break because streamed tool-call arguments are not always reconstructed into usable tool args before dispatch.

OpenClaw should robustly reconstruct streamed tool-call arguments for OpenAI-compatible backends before dispatching tools, including cases where:

OpenClaw sometimes dispatches the tool call with incomplete arguments, leading to errors such as:

PR fix notes

PR #70294: fix(agents): repair malformed tool-call args on openai-completions

Description (problem / solution / changelog)

Summary

  • Problem: self-hosted Kimi/SGLang routes on openai-completions could emit streamed tool-call arguments in shapes the existing repair wrapper knows how to fix, but the runner only enabled that wrapper for anthropic-messages + Kimi.
  • Why it matters: fragmented or malformed tool-call args could reach tool dispatch as empty or unusable objects, breaking the turn.
  • What changed: renamed the stale Anthropic-only gate to shouldRepairMalformedToolCallArguments, kept the existing Kimi provider behavior, and additionally enabled the repair wrapper for openai-completions transports. Added focused guard tests and generalized the repair log wording.
  • What did NOT change (scope boundary): this does not rewrite the repair algorithm itself, broaden the helper to every transport, or change tool-call ID handling.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #69672
  • Related #67551
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: the malformed-argument repair wrapper already existed, but run/attempt.ts only enabled it when params.model.api === "anthropic-messages" and the provider normalized to Kimi.
  • Missing detection / guardrail: there was no direct test locking in the enablement gate itself, only wrapper-behavior tests once the wrapper was already active.
  • Contributing context (if known): self-hosted Kimi/SGLang reports the problematic stream shape over openai-completions, so the repair path stayed dormant even though the runtime already had logic for this class of malformed stream.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • src/agents/pi-embedded-runner/run/attempt.tool-call-argument-repair.test.ts
    • src/agents/pi-embedded-runner/run/attempt.test.ts
  • Scenario the test should lock in: Kimi on anthropic-messages still enables repair, openai-completions now enables repair even for non-Kimi provider ids, and unrelated transports remain off.
  • Why this is the smallest reliable guardrail: the bug was in wrapper enablement, not in the repair parser itself.
  • Existing test that already covers this (if any): src/agents/pi-embedded-runner/run/attempt.test.ts already covers the live wrapper behavior after enablement.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

  • Self-hosted openai-completions backends such as Kimi/SGLang now get the existing malformed streamed tool-call argument repair path, reducing empty-argument or unusable-argument tool dispatch failures.

Diagram (if applicable)

Before:
[self-hosted openai-completions stream] -> [wrapper gate off] -> [malformed args reach tool dispatch]

After:
[self-hosted openai-completions stream] -> [wrapper gate on] -> [existing repair wrapper normalizes args]

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local pnpm workspace checkout
  • Model/provider: self-hosted Kimi/SGLang on openai-completions; existing Kimi anthropic-messages path preserved
  • Integration/channel (if any): N/A
  • Relevant config (redacted): N/A

Steps

  1. Run a model on openai-completions that streams fragmented tool-call arguments.
  2. Observe the embedded runner stream wrapper selection in run/attempt.ts.
  3. Verify the malformed-argument repair wrapper is now installed for that transport and existing repair tests remain green.

Expected

  • The runner enables malformed tool-call argument repair for openai-completions and preserves existing Kimi behavior.

Actual

  • Matches expected in local verification.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios:
    • pnpm check:changed
    • pnpm tsgo
    • pnpm test src/agents/pi-embedded-runner/run/attempt.tool-call-argument-repair.test.ts src/agents/pi-embedded-runner/run/attempt.test.ts
  • Edge cases checked:
    • normalized Kimi provider ids still return true on anthropic-messages
    • openai-completions now returns true even when the provider id is not normalized to Kimi
    • openai-responses remains disabled
  • What you did not verify:
    • live round-trip against a real external SGLang endpoint from this PR branch

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: enabling the wrapper for every openai-completions route is broader than a Kimi-only gate and could affect other OpenAI-compatible backends.
    • Mitigation: the wrapper already has targeted repair heuristics and existing tests remain green; unrelated non-openai-completions transports remain unchanged.
  • Risk: future parser changes might regress the enablement contract again.
    • Mitigation: the new focused gate tests lock in the intended transport/provider matrix.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • extensions/qa-lab/src/providers/image-generation.test.ts (modified, +0/-1)
  • extensions/qa-lab/src/suite-runtime-agent-media.test.ts (modified, +0/-1)
  • qa/scenarios/config/config-restart-capability-flip.md (modified, +5/-1)
  • src/agents/pi-embedded-runner.splitsdktools.test.ts (modified, +2/-4)
  • src/agents/pi-embedded-runner/compact.hooks.harness.ts (modified, +1/-1)
  • src/agents/pi-embedded-runner/compact.ts (modified, +4/-5)
  • src/agents/pi-embedded-runner/run/attempt.spawn-workspace.test-support.ts (modified, +0/-1)
  • src/agents/pi-embedded-runner/run/attempt.tool-call-argument-repair.test.ts (added, +40/-0)
  • src/agents/pi-embedded-runner/run/attempt.tool-call-argument-repair.ts (modified, +10/-3)
  • src/agents/pi-embedded-runner/run/attempt.ts (modified, +10/-9)
  • src/agents/pi-embedded-runner/tool-split.ts (modified, +0/-3)
  • src/plugin-sdk/provider-auth-runtime.ts (modified, +6/-1)

Code Example



---

[agent/embedded] read tool called without path: toolCallId=call_... argsType=object

---

{
  "delta": {
    "tool_calls": [
      {
        "id": "functions.read:0",
        "index": 0,
        "type": "function",
        "function": {
          "name": "read",
          "arguments": ""
        }
      }
    ]
  }
}

---

{"arguments":"{\"path\": \"/"}
{"arguments":"tmp"}
{"arguments":"/hello"}
{"arguments":".txt\"}"}
RAW_BUFFERClick to expand / collapse

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

When using a self-hosted Kimi model behind an OpenAI-compatible SGLang endpoint with OpenClaw’s openai-completions transport, tool-call conversations can break because streamed tool-call arguments are not always reconstructed into usable tool args before dispatch.

In my setup, this surfaced as tools being invoked with empty or malformed arguments, which then broke the turn.

Steps to reproduce

Minimal backend reproduction

This curl request produces the streaming shape I observed from Kimi/SGLang:

curl -N -sS http://YOUR_SGLANG_HOST:30000/v1/chat/completions
-H 'Authorization: Bearer YOUR_API_KEY'
-H 'Content-Type: application/json'
-d '{ "model": "mymodel", "messages": [ { "role": "system", "content": "You are a test harness. Return exactly one tool call to read with JSON args only. Do not answer in natural language." }, { "role": "user", "content": "Call the read tool exactly once with path /tmp/hello.txt" } ], "tools": [ { "type": "function", "function": { "name": "read", "description": "Read a file", "parameters": { "type": "object", "properties": { "path": { "type": "string" } }, "required": ["path"], "additionalProperties": false } } } ], "tool_choice": { "type": "function", "function": { "name": "read" } }, "temperature": 0, "stream": true }'

Expected behavior

OpenClaw should robustly reconstruct streamed tool-call arguments for OpenAI-compatible backends before dispatching tools, including cases where:

tool arguments arrive in multiple deltas the first delta contains empty arguments tool ids use nonstandard but valid strings like functions.read:0 reasoning deltas and tool deltas are interleaved

Actual behavior

OpenClaw sometimes dispatches the tool call with incomplete arguments, leading to errors such as:

read tool called without path

OpenClaw version

all for the last month at least

Operating system

MacOS

Install method

No response

Model

kimi 2.5 and 2.6

Provider / routing chain

sglang:latest (and :dev)

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Impact and severity

No response

Additional information

In failing runs, OpenClaw logs showed errors like:

[agent/embedded] read tool called without path: toolCallId=call_... argsType=object

That indicates the tool call reached the tool layer as an object, but without the required path field.

The conversation would then derail because the model believed it had called the tool correctly, but OpenClaw dispatched an empty or unusable argument object.

What I found

Direct API testing against the backend showed that Kimi/SGLang streams tool calls in an OpenAI-like format, but with some notable differences:

  • streamed reasoning arrives as reasoning_content
  • tool call ids look like functions.read:0
  • tool arguments are streamed in fragments across multiple chunks
  • the first tool-call delta may contain an empty arguments string, with later chunks appending the JSON pieces

Example streaming pattern from the backend:

{
  "delta": {
    "tool_calls": [
      {
        "id": "functions.read:0",
        "index": 0,
        "type": "function",
        "function": {
          "name": "read",
          "arguments": ""
        }
      }
    ]
  }
}

followed by deltas like:

{"arguments":"{\"path\": \"/"}
{"arguments":"tmp"}
{"arguments":"/hello"}
{"arguments":".txt\"}"}

This is close to OpenAI-compatible behavior, but OpenClaw appears to have trouble in some real runs reconstructing these streamed arguments reliably before tool dispatch.

Why I think this is an OpenClaw bug

There is already code in OpenClaw that attempts to repair malformed streamed tool-call arguments, but in my investigation that repair path was gated in a way that did not apply to my openai-completions self-hosted Kimi route.

So the failure is not necessarily that the backend is fully invalid. It looks like OpenClaw has an existing compatibility path for this class of problem, but it is not consistently active for self-hosted OpenAI-compatible Kimi/SGLang configurations.

extent analysis

TL;DR

OpenClaw's argument reconstruction for streamed tool calls from Kimi/SGLang backend may need adjustments to handle fragmented JSON arguments and empty initial arguments strings.

Guidance

  • Review OpenClaw's existing code for repairing malformed streamed tool-call arguments to ensure it applies to self-hosted Kimi routes with openai-completions transport.
  • Verify that the arguments field in the tool_calls delta is correctly concatenated from multiple chunks before dispatching the tool call.
  • Consider adding logging to track the reconstruction of arguments from streamed deltas to identify where the process fails.
  • Investigate if the issue is specific to certain Kimi/SGLang configurations or if it's a general problem with OpenClaw's handling of streamed tool calls.

Example

No specific code example is provided due to the complexity of the issue and the need for a deeper understanding of OpenClaw's internal workings. However, the example streaming pattern from the backend and the expected behavior described in the issue can serve as a starting point for debugging and testing.

Notes

The issue seems to be related to OpenClaw's compatibility with Kimi/SGLang's streaming format, which is similar but not identical to OpenAI's. The existing repair path in OpenClaw might not be sufficient or correctly applied for self-hosted Kimi configurations.

Recommendation

Apply a workaround by modifying OpenClaw to consistently apply its existing argument repair logic for self-hosted Kimi routes with openai-completions transport, ensuring that fragmented JSON arguments are correctly reconstructed before tool dispatch.

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

OpenClaw should robustly reconstruct streamed tool-call arguments for OpenAI-compatible backends before dispatching tools, including cases where:

tool arguments arrive in multiple deltas the first delta contains empty arguments tool ids use nonstandard but valid strings like functions.read:0 reasoning deltas and tool deltas are interleaved

Still need to ship something?

×6

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

Back to top recommendations

TRENDING