Skip to content

fix(inworld): guard voices JSON.parse against malformed API response bodies#98660

Merged
vincentkoc merged 1 commit into
openclaw:mainfrom
solodmd:fix/inworld-tts-json-guard
Jul 1, 2026
Merged

fix(inworld): guard voices JSON.parse against malformed API response bodies#98660
vincentkoc merged 1 commit into
openclaw:mainfrom
solodmd:fix/inworld-tts-json-guard

Conversation

@solodmd

@solodmd solodmd commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

extensions/inworld/tts.ts:248 calls JSON.parse(voicesBody) on the Inworld voices API response body without try/catch. Malformed JSON → unhandled SyntaxError → process crash.

Changes

  • extensions/inworld/tts.ts: wrap JSON.parse(voicesBody) in try/catch → throw descriptive Error("Inworld voices API returned malformed JSON")
  • extensions/inworld/tts.test.ts: tighten the malformed-voices regression test to assert the descriptive error message instead of bare .rejects.toThrow()

Label: bugfix | merge-risk: 🟢 minimal

Evidence

Real behavior proof — calls listInworldVoices through local HTTP response harness

$ node --import tsx test/_proof_inworld_voices_json_guard.mts

PASS: malformed JSON: throws descriptive error :: msg="Inworld voices API returned malformed JSON"
PASS: valid JSON: parsed correctly :: count=1 id=Sarah
PASS: empty voices: returns empty array :: count=0
PASS: API error: status preserved :: msg="Inworld voices API error (503): service unavailable"

[proof] 4 PASS, 0 FAIL

Unit test

$ pnpm exec vitest run extensions/inworld/tts.test.ts

 ✓ regression: malformed voices JSON under the cap throws descriptive error
 Test Files  1 passed (1)
      Tests  27 passed (27)

User Impact

If the Inworld voices API ever returns a non-JSON 200 response, users will see a descriptive "Inworld voices API returned malformed JSON" error instead of an unhandled SyntaxError that crashes the gateway or CLI process.

@solodmd
solodmd force-pushed the fix/inworld-tts-json-guard branch from d1d751c to 16a3c61 Compare July 1, 2026 14:10
@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 1, 2026, 11:15 AM ET / 15:15 UTC.

Summary
The PR wraps Inworld voice-list JSON parsing in a provider-owned malformed-JSON error and tightens the regression test to assert that message.

PR surface: Source +5, Tests +2. Total +7 across 2 files.

Reproducibility: yes. Source inspection shows current main parses the bounded voices response with raw JSON.parse, and the existing test harness can queue a malformed 200 response through listInworldVoices; I did not execute tests because this was a read-only review.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: serialized state: extensions/inworld/tts.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Next step before merge

  • No ClawSweeper repair job is needed; the patch is already narrow and the remaining action is ordinary maintainer review or merge.

Security
Cleared: Cleared: the diff only changes Inworld response parsing and a colocated test assertion, with no dependency, workflow, secret, package, install, or permission-surface changes.

Review details

Best possible solution:

Land this narrow plugin-local parse guard after normal maintainer review, keeping the fix confined to Inworld's voice-list response boundary.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main parses the bounded voices response with raw JSON.parse, and the existing test harness can queue a malformed 200 response through listInworldVoices; I did not execute tests because this was a read-only review.

Is this the best way to solve the issue?

Yes. A local try/catch at the voice-list parse boundary is the narrowest maintainable fix and matches the existing Inworld TTS malformed-stream handling without adding config, SDK surface, or shared helper churn.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against a5a8d992aecb.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body now includes copied terminal output from an after-fix local HTTP response harness that exercises the malformed Inworld voices response path and shows the new descriptive error.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body now includes copied terminal output from an after-fix local HTTP response harness that exercises the malformed Inworld voices response path and shows the new descriptive error.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P2: This is a focused plugin bug fix for malformed Inworld voice-list responses with limited blast radius.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body now includes copied terminal output from an after-fix local HTTP response harness that exercises the malformed Inworld voices response path and shows the new descriptive error.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body now includes copied terminal output from an after-fix local HTTP response harness that exercises the malformed Inworld voices response path and shows the new descriptive error.
Evidence reviewed

PR surface:

Source +5, Tests +2. Total +7 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 6 1 +5
Tests 1 4 2 +2
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 10 3 +7

What I checked:

  • Current main parse path: Current main reads the bounded Inworld voices response and then parses it with raw JSON.parse, so malformed 200 responses still surface an unowned parse error on the voice-list path. (extensions/inworld/tts.ts:248, a5a8d992aecb)
  • Caller surface: The Inworld speech provider exposes listVoices and delegates directly to listInworldVoices, so the changed parse site is on the provider voice-list entry point. (extensions/inworld/speech-provider.ts:165, a5a8d992aecb)
  • Sibling parse pattern: The same module already catches malformed TTS stream JSON and throws a provider-owned error, making the PR's voice-list local try/catch consistent with the existing Inworld error style. (extensions/inworld/tts.ts:175, a5a8d992aecb)
  • Regression coverage path: Current main already queues a malformed 200 voices response but only asserts that some error is thrown; the PR tightens that test to the new provider-owned message. (extensions/inworld/tts.test.ts:478, a5a8d992aecb)
  • Patch shape: The PR diff changes only extensions/inworld/tts.ts and its colocated test, adding a local parse guard without config, SDK, dependency, package, workflow, or broader provider-surface changes. (extensions/inworld/tts.ts:245, a8d0a1ff9228)
  • Contributor proof: The PR body includes copied terminal output from a local HTTP response harness that exercises malformed JSON, valid JSON, empty voices, and API-error paths through listInworldVoices, plus the focused Vitest run. (a8d0a1ff9228)

Likely related people:

  • Alix-007: GitHub history shows the bounded Inworld response-read PR changed the same voices-body parse and malformed-voices test area this PR now refines. (role: adjacent fix author; confidence: high; commits: 4c8470c0699c, dde714102644; files: extensions/inworld/tts.ts, extensions/inworld/tts.test.ts)
  • vincentkoc: Merged the bounded Inworld response-read PR and authored the decoded audio cap follow-up in the same Inworld TTS area. (role: adjacent fix merger / recent area contributor; confidence: high; commits: 4c8470c0699c, a3acea84d4f1; files: extensions/inworld/tts.ts, extensions/inworld/tts.test.ts)
  • cshape: Authored the merged PR that introduced the bundled Inworld speech provider, including listVoices, tts.ts, and the initial test coverage. (role: feature introducer; confidence: high; commits: 0bcb4c95c185, e0a5e6a239f1; files: extensions/inworld/tts.ts, extensions/inworld/tts.test.ts, extensions/inworld/speech-provider.ts)
  • steipete: Merged the initial Inworld provider PR and appears in later test and provider cleanup history for the same files. (role: initial PR merger / adjacent test contributor; confidence: medium; commits: 0bcb4c95c185, bf273c451aef, 4fa5092cdc39; files: extensions/inworld/tts.ts, extensions/inworld/tts.test.ts, extensions/inworld/speech-provider.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 1, 2026
@solodmd
solodmd force-pushed the fix/inworld-tts-json-guard branch 2 times, most recently from dffac9d to 54803de Compare July 1, 2026 14:47
@solodmd
solodmd force-pushed the fix/inworld-tts-json-guard branch from 54803de to a8d0a1f Compare July 1, 2026 14:48
@solodmd

solodmd commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 1, 2026
@vincentkoc
vincentkoc merged commit c914f89 into openclaw:main Jul 1, 2026
112 of 118 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 2, 2026
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
sheyanmin pushed a commit to sheyanmin/openclaw that referenced this pull request Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: inworld Extension: inworld P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: XS status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants