openclaw - ✅(Solved) Fix Tool call JSON parse errors exposed to user after session compaction [1 pull requests, 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#59080Fetched 2026-04-08 02:28:55
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Participants
Timeline (top)
cross-referenced ×2

This was observed after a session compaction event. The model's first tool call post-compaction had only ~25 tokens of malformed JSON arguments. The error was passed through errorMessage and delivered to the chat channel.

Related: #50292 (suppress intermediate text output)

Error Message

After session compaction, the model occasionally generates malformed tool call arguments (invalid JSON). When this happens, the framework surfaces the raw JSON.parse error message directly to the user as a chat message. Example error message sent to user: 3. The JSON.parse error is sent as a user-visible message instead of being handled gracefully The raw JavaScript JSON.parse error message is delivered to the user as a chat message. This exposes internal framework errors that users should never see. This was observed after a session compaction event. The model's first tool call post-compaction had only ~25 tokens of malformed JSON arguments. The error was passed through errorMessage and delivered to the chat channel.

Root Cause

This was observed after a session compaction event. The model's first tool call post-compaction had only ~25 tokens of malformed JSON arguments. The error was passed through errorMessage and delivered to the chat channel.

Related: #50292 (suppress intermediate text output)

Fix Action

Fixed

PR fix notes

PR #59118: fix: suppress raw JSON parse errors from leaking to Discord channels (#59076) [AI-assisted]

Description (problem / solution / changelog)

When streaming tool calls with long CJK text, the Anthropic SDK's SSE parser can throw SyntaxError. This error was forwarded as-is to the Discord channel instead of being caught and rewritten to a user-friendly message.

Summary

  • Problem: Raw JSON parse errors (e.g. Expected ',' or '}' after property value in JSON at position 334) from the Anthropic SDK streaming layer are leaked to Discord channels as standalone messages.
  • Why it matters: Users see confusing, technical error messages in their Discord channels that provide no actionable information. The tool call still recovers and the response comes through, making this a cosmetic but disruptive UX issue.
  • What changed: Added isStreamingJsonParseError() detector in pi-embedded-helpers/errors.ts that intercepts JSON parse error patterns in both formatAssistantErrorText() and sanitizeUserFacingText(), replacing them with a friendly "LLM streaming response contained a malformed fragment. Please try again." message.
  • What did NOT change (scope boundary): No changes to the streaming pipeline itself, the Anthropic SDK, pi-ai library, or the Discord channel plugin. The fix is purely in the error formatting layer that sits between the agent runner and the channel delivery.

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 #59076
  • Related #59080 (similar error-leaking pattern after session compaction)
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

  • Root cause: formatAssistantErrorText() in pi-embedded-helpers/errors.ts has a comprehensive pattern-matching chain for known error types (context overflow, billing, rate limit, role ordering, transport, etc.), but lacked a pattern for JSON parse errors from streaming SSE data. Unrecognized errors fall through to L949 which returns the raw error text (truncated to 600 chars).
  • Missing detection / guardrail: No pattern existed for SyntaxError messages from JSON.parse failures in the streaming pipeline.
  • Prior context: The error originates in @anthropic-ai/sdk/src/core/streaming.ts L69-75 where JSON.parse(sse.data) is called on each SSE event. The SDK re-throws the SyntaxError, which pi-ai's streamAnthropic catch block (L340) captures and converts to output.errorMessage. This flows through the agent lifecycle handler to formatAssistantErrorText().
  • Why this regressed now: Issue reporter notes it appeared after upgrading to 2026.3.31. Likely a change in the Anthropic SDK or SSE payload format for CJK content made the SSE data parsing more fragile.
  • If unknown, what was ruled out: The parseStreamingJson() function in pi-ai (used for input_json_delta partial JSON) is safe (triple-fallback). The problem is in the SDK's SSE layer, which parses the raw SSE data: line with strict JSON.parse before pi-ai ever sees the event.

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-helpers.formatassistanterrortext.test.ts
  • Scenario the test should lock in: formatAssistantErrorText returns a friendly message (not raw error text) when the error matches JSON parse error patterns from streaming SDKs.
  • Why this is the smallest reliable guardrail: The formatAssistantErrorText function is the last-mile sanitizer before error text reaches channels. Testing at this layer catches all upstream error sources without needing to mock the full streaming pipeline.
  • Existing test that already covers this (if any): None — this is a new error pattern class.
  • If no new test is added, why not: 5 new tests added covering 3 JSON parse error variants + 2 sanitizeUserFacingText context tests.

User-visible / Behavior Changes

  • Before: Raw JSON parse errors like Expected ',' or '}' after property value in JSON at position 334 (line 1 column 335) appear as standalone Discord messages.
  • After: These errors are rewritten to "LLM streaming response contained a malformed fragment. Please try again." — a clear, non-technical message that tells the user what happened and what to do.

Diagram (if applicable)

Before:
  SDK SyntaxError → pi-ai catch → errorMessage → formatAssistantErrorText (no match) → raw text → Discord channel ❌

After:
  SDK SyntaxError → pi-ai catch → errorMessage → formatAssistantErrorText (isStreamingJsonParseError ✓) → friendly text → Discord channel ✅

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)

Repro + Verification

Environment

  • OS: Darwin 23.4.0 arm64
  • Runtime/container: Node v22.18.0, pnpm 10.28.0
  • Model/provider: Anthropic Claude Opus 4
  • Integration/channel (if any): Discord
  • Relevant config (redacted): Channel with [skills: legal, lawclaw] in topic (adds ~10KB of skill context with regex patterns)

Steps

  1. Create a Discord channel with [skills: legal, lawclaw] in the topic
  2. Send a message that triggers the agent to use Edit or Write with substantial Chinese text content
  3. Observe the raw JSON parse error appearing as a Discord message before the actual response

Expected

  • JSON parse errors from the streaming partial JSON parser should be caught internally and not forwarded to the Discord channel.

Actual

  • Raw error messages like Expected ',' or '}' after property value in JSON at position 334 (line 1 column 335) appear as standalone Discord messages.

Evidence

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

5 new test cases added and passing:

  • sanitizes streaming JSON parse errors from Anthropic SDK (#59076) — verifies Expected ',' or '}' ... pattern
  • sanitizes 'Expected double-quoted property name' JSON parse errors (#59076) — verifies Expected double-quoted... pattern
  • sanitizes 'Unexpected token' JSON parse errors (#59076) — verifies Unexpected token... pattern
  • sanitizeUserFacingText: rewrites JSON parse error in error context — verifies sanitize path
  • sanitizeUserFacingText: does not rewrite when not in error context — ensures normal text is not affected

All 131 related tests pass. pnpm build succeeds. AI-generated code verified by automated test runs.

Human Verification (required)

  • Verified scenarios: All 5 new test cases + 126 existing tests in the error formatting suite pass. Build verification passed.
  • Edge cases checked: (1) JSON parse error with various position/line formats, (2) sanitizeUserFacingText only rewrites in error context (doesn't affect normal assistant text mentioning JSON), (3) regex pattern doesn't false-positive on unrelated error messages.
  • What you did NOT verify: Live Discord channel testing with actual CJK streaming tool calls — requires a running gateway with Discord integration and an Anthropic API key.

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)

Risks and Mitigations

  • Risk: The regex \b(?:expected|unexpected)\b.+\bin json\b.+\bposition\b could theoretically match an assistant message that happens to contain all three keywords in sequence (e.g., explaining JSON parsing to a user).
    • Mitigation: The pattern only fires inside formatAssistantErrorText() (which processes stopReason: "error" assistant messages) and inside sanitizeUserFacingText() only when errorContext: true. Normal assistant text is never routed through these error-handling paths. Additionally, the regex requires all three keywords (expected/unexpected, in JSON, position) in a single line, making false positives on natural language extremely unlikely.

Changed files

  • src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts (modified, +45/-0)
  • src/agents/pi-embedded-helpers/errors.ts (modified, +27/-0)

Code Example

Expected ':' after property name in JSON at position 10 (line 1 column 11)
RAW_BUFFERClick to expand / collapse

Bug Description

After session compaction, the model occasionally generates malformed tool call arguments (invalid JSON). When this happens, the framework surfaces the raw JSON.parse error message directly to the user as a chat message.

Example error message sent to user:

Expected ':' after property name in JSON at position 10 (line 1 column 11)

Steps to Reproduce

  1. Have a long session that triggers compaction
  2. After compaction, the model may generate a tool_use block with malformed JSON arguments (e.g., only 25 tokens output before the JSON becomes invalid)
  3. The JSON.parse error is sent as a user-visible message instead of being handled gracefully

Observed Behavior

The raw JavaScript JSON.parse error message is delivered to the user as a chat message. This exposes internal framework errors that users should never see.

Expected Behavior

  1. Internal errors should never be exposed to users. JSON parse failures on tool call arguments should be caught and handled gracefully (e.g., logged internally, retried, or presented as a user-friendly "something went wrong" message).
  2. Consider retrying when the model generates malformed tool call JSON, especially after compaction, since this appears to be a transient issue.

Environment

  • Model: claude-opus-4-6 via nex provider (anthropic-messages API)
  • Trigger: Session compaction (context refresh)
  • Framework version: current
  • Channel: Feishu

Context

This was observed after a session compaction event. The model's first tool call post-compaction had only ~25 tokens of malformed JSON arguments. The error was passed through errorMessage and delivered to the chat channel.

Related: #50292 (suppress intermediate text output)

extent analysis

TL;DR

Catch and handle JSON.parse errors internally to prevent exposing raw error messages to users.

Guidance

  • Identify where the JSON.parse error is being caught and modify the code to handle the error gracefully, such as logging it internally or presenting a user-friendly error message.
  • Consider implementing a retry mechanism for tool call arguments that fail JSON parsing, especially after session compaction.
  • Review the model's output after compaction to ensure it is generating valid JSON arguments.
  • Verify that the framework is correctly handling errors and not passing them through to the user-visible chat message.

Example

try {
  const toolCallArgs = JSON.parse(args);
  // process toolCallArgs
} catch (error) {
  // log error internally or present user-friendly error message
  console.error('Error parsing tool call arguments:', error);
  // optional: retry or fallback to default behavior
}

Notes

This solution assumes that the JSON.parse error is being caught and passed through to the user-visible chat message. The exact implementation may vary depending on the framework and model being used.

Recommendation

Apply workaround: Implement error handling for JSON.parse errors to prevent exposing internal framework errors to users. This will improve the user experience and prevent potential security issues.

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING