Skip to content

fix(kimi): preserve valid Anthropic-compatible toolCall arguments in malformed-args repair path#54491

Merged
Takhoffman merged 3 commits into
openclaw:mainfrom
yuanaichi:fix/kimi-toolcall-args
Mar 29, 2026
Merged

fix(kimi): preserve valid Anthropic-compatible toolCall arguments in malformed-args repair path#54491
Takhoffman merged 3 commits into
openclaw:mainfrom
yuanaichi:fix/kimi-toolcall-args

Conversation

@yuanaichi

@yuanaichi yuanaichi commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Problem: When using Kimi in Anthropic-compatible (anthropic-messages) mode, the malformed toolCall-arguments repair logic could fail to preserve otherwise-valid JSON arguments (resulting in missing/empty tool arguments).
  • Why it matters: Tool execution depends on correct toolCall.arguments; losing valid arguments can cause wrong tool behavior, failures, or unexpected “empty args” executions.
  • What changed: Adjusted the Kimi/Anthropic toolCall-argument repair path to keep valid, complete JSON arguments intact and only apply “balanced-prefix + short trailing junk” repair when the arguments are actually malformed.
  • What did NOT change (scope boundary): No changes to tool dispatch logic, tool allowlists, transcript storage, or non-Kimi providers.

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

Root Cause / Regression History (if applicable)

  • Root cause: The malformed-args repair wrapper for Kimi/Anthropic streams treated some valid toolCall argument payloads as candidates for repair, and under certain stream delta patterns it could clear/override arguments instead of preserving the complete JSON object.
  • Missing detection / guardrail: Unit coverage focused on “valid JSON + short trailing junk” but didn’t adequately lock in “valid JSON must remain unchanged” across all relevant stream delta/event sequences.
  • Prior context (git blame, prior PR, issue, or refactor if known): Unknown.
  • Why this regressed now: Likely triggered by Kimi’s Anthropic-compatible streaming behavior (toolcall deltas containing } early / nested JSON / event chunking), which increased the chance the heuristic attempted repair at the wrong time.
  • If unknown, what was ruled out: Not an auth/key issue and not a tool registry/dispatch mismatch; reproduces at the stream parsing/repair layer.

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.test.ts (malformed toolCall args repair tests)
  • Scenario the test should lock in: When toolCall arguments are already valid JSON (including nested objects), the repair path must not alter or clear them; when valid JSON is followed by 1–3 trailing junk characters, it should repair to the valid object.
  • Why this is the smallest reliable guardrail: This bug is purely in the stream repair/parsing layer; unit tests can deterministically simulate deltas/events without requiring a live model.
  • Existing test that already covers this (if any): The suite includes a test for “trailing junk follows valid JSON”; this PR strengthens/extends coverage to ensure valid JSON remains intact.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

None (except improved reliability of tool execution for Kimi Anthropic-compatible runs).

Security Impact (required)

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

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node.js (local)
  • Model/provider: kimi-coding/k2p5
  • Integration/channel (if any): N/A
  • Relevant config (redacted): Provider set to kimi, model API anthropic-messages

Steps

  1. Configure provider kimi with Anthropic-compatible API (anthropic-messages).
  2. Trigger a tool call where arguments are a valid nested JSON object (e.g. {"path":{"a":"/tmp/report.txt"}}) and/or where streaming deltas include early } tokens.
  3. Observe tool call arguments during/after stream assembly.

Expected

  • Valid JSON arguments are preserved exactly and passed to tool execution unchanged.

Actual

  • Arguments could be cleared/overwritten in the repair path, resulting in incomplete or empty arguments passed to tools.

Evidence

Attach at least one:

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

Human Verification (required)

  • Verified scenarios: Ran the unit tests for the malformed toolCall-arguments repair and confirmed that the final assembled message preserves valid arguments; also confirmed that the “short trailing junk” repair still works. Before the fix, with Kimi in Anthropic-messages streaming mode, multiple consecutive tool_call invocations could carry empty arguments; after the fix, empty-argument tool calls no longer occur.
  • Edge cases checked: -
  • What you did not verify: -

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.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

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

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert this commit; alternatively disable the Kimi/Anthropic malformed-args repair wrapper if maintainers prefer.
  • Files/config to restore: src/agents/pi-embedded-runner/run/attempt.ts
  • Known bad symptoms reviewers should watch for: Tool calls executed with {}/missing required parameters; intermittent tool failures only on Kimi Anthropic-compatible runs.

Risks and Mitigations

  • Risk: Repair logic might still be triggered too aggressively for some unusual streaming chunk patterns.
    • Mitigation: Keep repair gated to Kimi + anthropic-messages, retain strict trailing-suffix limits, and add/extend unit coverage for representative delta sequences.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Mar 25, 2026
@greptile-apps

greptile-apps Bot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a narrow but impactful bug in the Kimi/Anthropic-compatible stream-repair path: previously, tryParseMalformedToolCallArguments returned undefined for valid JSON, causing the caller to invoke clearToolCallArgumentsInMessage and wipe out already-correct tool arguments. The fix returns the parsed result instead, so arguments are preserved.

Key changes:

  • attempt.ts: One-line logic change in tryParseMalformedToolCallArguments — valid JSON now returns { args: parsed, trailingSuffix: "" } instead of undefined, preventing the caller from clearing arguments.
  • attempt.test.ts: New test reproduces the split-delta pattern ({"path":"/tmp/report.txt" + }) and asserts that all tool-call argument references in partial/streamed/end/final messages are correctly populated.

Two non-blocking follow-ups worth considering:

  • The new try path skips the parsed && typeof parsed === "object" && !Array.isArray(parsed) guard present in the catch path, creating a minor type inconsistency for edge-case non-object JSON.
  • log.warn("repairing ... after 0 trailing chars") now fires on every normal, well-formed tool call, adding log noise; the warning should be gated on repair.trailingSuffix.length > 0.

Confidence Score: 4/5

  • Safe to merge; the fix is narrowly scoped, correct, and well-tested — two P2 suggestions are non-blocking.
  • The core logic change is clearly correct (returning the parsed value instead of undefined prevents unintended argument clearing). The new test covers the exact regression scenario. The two flagged items — missing non-object type guard and spurious log.warn on valid JSON — are quality/observability concerns that don't affect correctness in the common case. Score of 4 rather than 5 reflects those small follow-up gaps.
  • Logging path around line 1294 in attempt.ts warrants a quick check to avoid warn-on-every-valid-toolcall noise in production.

Comments Outside Diff (1)

  1. src/agents/pi-embedded-runner/run/attempt.ts, line 1294-1299 (link)

    P2 Misleading warning log fires for valid (non-repaired) JSON

    With this fix, tryParseMalformedToolCallArguments now returns { args, trailingSuffix: "" } for already-valid JSON, so repair is truthy even when no actual repair was necessary. This causes log.warn("repairing Kimi tool call arguments after 0 trailing chars") to fire on every normal, well-formed tool call from Kimi in Anthropic-compatible mode — adding noise to production logs and making it harder to spot genuine repairs (trailing junk cases) by drowning them out.

    Consider only warning when an actual repair was needed (i.e. trailingSuffix.length > 0):

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/agents/pi-embedded-runner/run/attempt.ts
    Line: 1294-1299
    
    Comment:
    **Misleading warning log fires for valid (non-repaired) JSON**
    
    With this fix, `tryParseMalformedToolCallArguments` now returns `{ args, trailingSuffix: "" }` for already-valid JSON, so `repair` is truthy even when no actual repair was necessary. This causes `log.warn("repairing Kimi tool call arguments after 0 trailing chars")` to fire on every normal, well-formed tool call from Kimi in Anthropic-compatible mode — adding noise to production logs and making it harder to spot genuine repairs (trailing junk cases) by drowning them out.
    
    Consider only warning when an actual repair was needed (i.e. `trailingSuffix.length > 0`):
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/run/attempt.ts
Line: 1152-1153

Comment:
**Missing type guard for non-object valid JSON**

The `catch` path (line 1169) guards against `null`, arrays, and primitives before casting to `Record<string, unknown>`:
```typescript
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
  ? { args: parsed as Record<string, unknown>, trailingSuffix: suffix }
  : undefined;
```

The new `try` path skips this guard and unconditionally casts. In practice, tool-call arguments are always objects, and `shouldAttemptMalformedToolCallRepair` only triggers on `}` / `]` deltas, so the risk is low. However, if `raw` is a valid JSON array (e.g. `[1,2]`) the closing `]` would trigger the repair path, and `parsed as Record<string, unknown>` would silently hold an array.

For consistency with the catch path and to avoid a surprising upstream `typedBlock.arguments = [...]` mutation:

```suggestion
    const parsed = JSON.parse(raw) as unknown;
    if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
      return undefined;
    }
    return { args: parsed as Record<string, unknown>, trailingSuffix: "" };
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/run/attempt.ts
Line: 1294-1299

Comment:
**Misleading warning log fires for valid (non-repaired) JSON**

With this fix, `tryParseMalformedToolCallArguments` now returns `{ args, trailingSuffix: "" }` for already-valid JSON, so `repair` is truthy even when no actual repair was necessary. This causes `log.warn("repairing Kimi tool call arguments after 0 trailing chars")` to fire on every normal, well-formed tool call from Kimi in Anthropic-compatible mode — adding noise to production logs and making it harder to spot genuine repairs (trailing junk cases) by drowning them out.

Consider only warning when an actual repair was needed (i.e. `trailingSuffix.length > 0`):

```suggestion
                  if (!loggedRepairIndices.has(event.contentIndex) && repair.trailingSuffix.length > 0) {
                    loggedRepairIndices.add(event.contentIndex);
                    log.warn(
                      `repairing Kimi tool call arguments after ${repair.trailingSuffix.length} trailing chars`,
                    );
                  }
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(kimi): preserve valid Anthropic-comp..." | Re-trigger Greptile

Comment on lines +1152 to +1153
const parsed = JSON.parse(raw);
return { args: parsed as Record<string, unknown>, trailingSuffix: "" };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing type guard for non-object valid JSON

The catch path (line 1169) guards against null, arrays, and primitives before casting to Record<string, unknown>:

return parsed && typeof parsed === "object" && !Array.isArray(parsed)
  ? { args: parsed as Record<string, unknown>, trailingSuffix: suffix }
  : undefined;

The new try path skips this guard and unconditionally casts. In practice, tool-call arguments are always objects, and shouldAttemptMalformedToolCallRepair only triggers on } / ] deltas, so the risk is low. However, if raw is a valid JSON array (e.g. [1,2]) the closing ] would trigger the repair path, and parsed as Record<string, unknown> would silently hold an array.

For consistency with the catch path and to avoid a surprising upstream typedBlock.arguments = [...] mutation:

Suggested change
const parsed = JSON.parse(raw);
return { args: parsed as Record<string, unknown>, trailingSuffix: "" };
const parsed = JSON.parse(raw) as unknown;
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
return undefined;
}
return { args: parsed as Record<string, unknown>, trailingSuffix: "" };
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/run/attempt.ts
Line: 1152-1153

Comment:
**Missing type guard for non-object valid JSON**

The `catch` path (line 1169) guards against `null`, arrays, and primitives before casting to `Record<string, unknown>`:
```typescript
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
  ? { args: parsed as Record<string, unknown>, trailingSuffix: suffix }
  : undefined;
```

The new `try` path skips this guard and unconditionally casts. In practice, tool-call arguments are always objects, and `shouldAttemptMalformedToolCallRepair` only triggers on `}` / `]` deltas, so the risk is low. However, if `raw` is a valid JSON array (e.g. `[1,2]`) the closing `]` would trigger the repair path, and `parsed as Record<string, unknown>` would silently hold an array.

For consistency with the catch path and to avoid a surprising upstream `typedBlock.arguments = [...]` mutation:

```suggestion
    const parsed = JSON.parse(raw) as unknown;
    if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
      return undefined;
    }
    return { args: parsed as Record<string, unknown>, trailingSuffix: "" };
```

How can I resolve this? If you propose a fix, please make it concise.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7ea8415387

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1152 to +1153
const parsed = JSON.parse(raw);
return { args: parsed as Record<string, unknown>, trailingSuffix: "" };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject non-object JSON in repaired tool arguments

tryParseMalformedToolCallArguments now treats any JSON.parse(raw) success as repairable and casts it to Record<string, unknown>, but this skips the object-shape guard used in the malformed-prefix path. If Kimi streams a valid non-object payload like [] (or another primitive containing ]/} in deltas), this code will overwrite toolCall.arguments with a non-object value, which violates the tool-call argument contract and can cause downstream tool validation/execution failures. Add the same typeof parsed === "object" && !Array.isArray(parsed) check before returning.

Useful? React with 👍 / 👎.

@taath-wyx

Copy link
Copy Markdown

This fixed it! Hot-patched v3.24 locally and tool calls are working again. Much appreciated!

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bca9b573bc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1591 to +1594
expect(partialToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
expect(streamedToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
expect(endMessageToolCall.arguments).toEqual({ path: "/tmp/report.txt" });
expect(finalToolCall.arguments).toEqual({ path: "/tmp/report.txt" });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include production fix for new no-junk toolcall test

This commit adds a test that expects complete JSON args to be preserved, but the implementation in attempt.tool-call-argument-repair.ts still returns undefined when JSON.parse(raw) succeeds and then clears tool arguments on that path, so this new assertion will fail. In other words, the commit introduces a failing test without the corresponding runtime change, which will block CI until the repair logic is updated in the same patch series.

Useful? React with 👍 / 👎.

@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: XL and removed size: XS scripts Repository scripts labels Mar 27, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ab10b7576

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const SESSIONS_YIELD_ABORT_SETTLE_TIMEOUT_MS = process.env.OPENCLAW_TEST_FAST === "1" ? 250 : 2_000;

// Persist a hidden context reminder so the next turn knows why the runner stopped.
export function buildSessionsYieldContextMessage(message: string): string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Remove redeclarations that shadow imported helpers

This commit pastes full helper implementations into attempt.ts without removing the existing imports of the same symbols (for example buildSessionsYieldContextMessage is imported from attempt.sessions-yield.js and then declared again here). In ESM/TypeScript modules, redeclaring an imported binding is a hard module parse error, so importing this file will fail before runtime and block the runner entirely.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab91004a2f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const SESSIONS_YIELD_ABORT_SETTLE_TIMEOUT_MS = process.env.OPENCLAW_TEST_FAST === "1" ? 250 : 2_000;

// Persist a hidden context reminder so the next turn knows why the runner stopped.
export function buildSessionsYieldContextMessage(message: string): string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Remove duplicated helper declarations in attempt runner

attempt.ts now redeclares helpers like buildSessionsYieldContextMessage that are already imported from ./attempt.sessions-yield.js, which creates duplicate top-level bindings in the same ES module. In this state, loading or typechecking the runner fails with an identifier-already-declared parse error before any runtime logic executes, so the embedded runner cannot start in any environment.

Useful? React with 👍 / 👎.

@yuanaichi
yuanaichi force-pushed the fix/kimi-toolcall-args branch from ab91004 to 947f2b2 Compare March 27, 2026 06:34

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2fd49017f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +83 to +84
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
? { args: parsed as Record<string, unknown>, trailingSuffix: "" }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Invalidate cached repair when long trailing junk arrives

Returning a repair for any fully parseable object at this point caches arguments as soon as the first balanced } is seen, but that cache is not revoked if a later delta appends malformed trailing text longer than the 3-character allowance. In a stream like {"path":"/tmp/report.txt"} followed by a separate oops delta, shouldAttemptMalformedToolCallRepair skips re-validation on the second chunk (trimmedDelta.length > 3), so toolcall_end still applies the stale cached args even though total trailing junk is out of policy; this changes behavior from “do not repair when junk >3” to “repair anyway if junk arrives in a later long chunk.”

Useful? React with 👍 / 👎.

@ahmaddiqi

Copy link
Copy Markdown

Confirmed this bug on OpenClaw 2026.3.24 running on a self-hosted VPS with kimi/kimi-code. Tool calls were consistently receiving empty {}\} arguments even when Kimi had streamed valid JSON — traced it back to tryParseMalformedToolCallArgumentsreturningundefinedon successful parse, which sent the caller down theclearToolCallArgumentsInMessage` path.

Applied the fix from this PR manually to the dist file and it resolved the issue immediately. Would be great to get this merged and released. 🙏

@yuanaichi yuanaichi changed the title fix(kimi): preserve valid Anthropic-compatible toolCall arguments in malformed-args repair fix(kimi): preserve valid Anthropic-compatible toolCall arguments in malformed-args repair path Mar 27, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e203d731e0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +82 to +84
const parsed = JSON.parse(raw) as unknown;
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
? { args: parsed as Record<string, unknown>, trailingSuffix: "" }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Revalidate cached args after long trailing junk deltas

This new JSON.parse(raw) success path returns a repair for already-valid JSON and therefore caches arguments immediately, but that cache is not invalidated when a later malformed suffix arrives as a single chunk longer than 3 characters. In wrapStreamRepairMalformedToolCallArguments, shouldAttemptMalformedToolCallRepair skips re-parse when trimmedDelta.length > 3, so a stream like valid object then oops keeps the stale cached args and still applies them at toolcall_end, which violates the “no repair when trailing junk exceeds allowance” policy.

Useful? React with 👍 / 👎.

@igmarketing

Copy link
Copy Markdown

Thanks man for having fixed this! That's a big one... Hopefully it will be applied to next release!

@yuanaichi

Copy link
Copy Markdown
Contributor Author

Thanks man for having fixed this! That's a big one... Hopefully it will be applied to next release!

Thanks for your comment. I hope this can be merged into the next release. This PR is base on OpenClaw main branch, If you’d like to apply the fix on v2026.3.24, you can clone my OpenClaw fork and check out the fix/v2026.3.24-kimi-code-toolcall-empty-args branch.

@heyAyushh

Copy link
Copy Markdown

I think it is failing in the latest version as well v2026.3.28

@Takhoffman
Takhoffman force-pushed the fix/kimi-toolcall-args branch from e203d73 to ce417d4 Compare March 29, 2026 03:16
@Takhoffman
Takhoffman force-pushed the fix/kimi-toolcall-args branch from 48cfe12 to d9d7109 Compare March 29, 2026 03:37
@Takhoffman
Takhoffman merged commit ec7f19e into openclaw:main Mar 29, 2026
9 checks passed
@Takhoffman

Copy link
Copy Markdown
Contributor

Merged in ec7f19e after verification with pnpm build, pnpm check, and pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts on the final rebased head. I made follow-up updates on the PR branch before merge to keep already-valid tool-call JSON intact, revalidate cached repairs when later trailing junk arrives, and preserve the newer malformed-preamble repair coverage after rebasing onto the latest main. The changelog entry is in CHANGELOG.md under ### Fixes in ## Unreleased.

Alix-007 pushed a commit to Alix-007/openclaw that referenced this pull request Mar 30, 2026
…malformed-args repair path (openclaw#54491)

Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Doraemon-Claw pushed a commit to Doraemon-Claw/openclaw that referenced this pull request Mar 30, 2026
…malformed-args repair path (openclaw#54491)

Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
alexjiang1 pushed a commit to alexjiang1/openclaw that referenced this pull request Mar 31, 2026
…malformed-args repair path (openclaw#54491)

Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
…malformed-args repair path (openclaw#54491)

Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
LKbaba added a commit to LKbaba/openclaw that referenced this pull request Apr 3, 2026
tryParseMalformedToolCallArguments 中 JSON.parse(raw) 成功后返回
undefined,触发 clearToolCallArgumentsInMessage 清空参数为 {}。
修复:合法 JSON 返回 { args: parsed, trailingSuffix: "" }。

补丁脚本从 5 步升级到 6 步(B/I/J/K/L)。

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@steipete steipete mentioned this pull request Apr 4, 2026
25 tasks
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…malformed-args repair path (openclaw#54491)

Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Tardisyuan pushed a commit to Tardisyuan/openclaw that referenced this pull request Apr 30, 2026
…malformed-args repair path (openclaw#54491)

Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…malformed-args repair path (openclaw#54491)

Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…malformed-args repair path (openclaw#54491)

Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…malformed-args repair path (openclaw#54491)

Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Nachx639 pushed a commit to Nachx639/clawdbot that referenced this pull request Jun 17, 2026
…malformed-args repair path (openclaw#54491)

Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment