#23648: fix: detect truncated file paths from partial JSON streaming
agents
size: S
Cluster:
Error Handling in Agent Tools
## Problem
Fixes #23622
When a model calls the `edit` tool with a relatively long file path, the streaming JSON accumulation can silently truncate string values. The `partial-json` library used by `parseStreamingJson` closes unterminated strings on the `done` boundary, producing paths like `README.m` instead of `README.md`. This causes confusing "file not found" errors (or JSON parse errors when the truncation includes leaked JSON structure like trailing commas).
## Root Cause
The upstream `pi-ai` streaming layer uses `partial-json` to parse incomplete JSON during streaming. This is correct for progressive display during streaming, but on the final `done`/`content_block_stop` event, the same forgiving parser is used — silently truncating values instead of reporting corruption.
## Fix
Add a `detectTruncatedPath` heuristic in OpenClaw's tool wrapper layer that catches:
- **Single-character extensions** that aren't known valid ones (`.c`, `.h`, `.r`, `.d`, `.v`, `.o`, `.a`, `.s`, `.S`, `.R`) — e.g. `.m` (truncated `.md`), `.t` (truncated `.ts`), `.j` (truncated `.js`)
- **Leaked JSON structure** — trailing commas/spaces from partial parsing (e.g. `README.m, `)
When detected, throws a descriptive `parameterValidationError` that tells the model the path appears truncated and to retry with the complete path. This is applied to all file-based tool wrappers:
- `wrapToolParamNormalization` (edit, write)
- `wrapToolWorkspaceRootGuard` (sandboxed tools)
- `createOpenClawReadTool` (read)
## Testing
Added 29 unit tests covering:
- Valid paths with multi-char extensions (not flagged)
- Legitimate single-char extensions like `.c`, `.h`, `.R` (not flagged)
- Truncated paths with suspicious single-char extensions (flagged)
- Paths with leaked JSON commas (flagged)
- Edge cases (empty strings, dotfiles, no extension)
```
✓ src/agents/pi-tools.truncated-path-detection.test.ts (29 tests) 3ms
```
## Note
The ideal long-term fix is in the `pi-ai` streaming layer: use strict `JSON.parse` on the `done` event and only fall back to `partial-json` during streaming deltas. This PR provides a defensive check at the OpenClaw layer that catches the symptom regardless of the upstream fix timeline.
---
*This PR was AI-assisted (per CONTRIBUTING.md guidelines).*
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR adds a defensive heuristic (`detectTruncatedPath`) to catch file paths silently truncated by the `partial-json` library during streaming tool call argument parsing, throwing a descriptive retry error instead of a confusing "file not found." The guard is applied at all three tool wrapper entry points (`wrapToolParamNormalization`, `wrapToolWorkspaceRootGuard`, `createOpenClawReadTool`).
- **Missing `.m` extension in allowlist**: The `VALID_SINGLE_CHAR_EXTENSIONS` set omits `.m` (Objective-C / MATLAB), a widely-used extension — especially relevant since this project includes iOS/macOS apps. This will cause false positive truncation errors when users work with `.m` files.
- **Test file duplicates production code**: The test suite copies the `detectTruncatedPath` function and allowlist instead of importing them, meaning tests validate a separate copy rather than the actual production logic. Future drift between the two would go undetected.
<h3>Confidence Score: 3/5</h3>
- The heuristic approach is sound but has a false positive gap for Objective-C `.m` files that should be fixed before merging.
- The core idea is good and well-placed in the code, but the missing `.m` extension in the allowlist will cause false positives for a common file type. The test duplication issue reduces confidence that the tests will catch regressions in the actual production code.
- `src/agents/pi-tools.read.ts` (missing `.m` in `VALID_SINGLE_CHAR_EXTENSIONS`), `src/agents/pi-tools.truncated-path-detection.test.ts` (tests don't import production code)
<sub>Last reviewed commit: 5f9bd56</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#19394: fix(agents): normalize tool call arguments dropped to {} (#19261)
by DevvGwardo · 2026-02-17
80.7%
#11210: Harden Tool-Call Streaming Parser for OpenAI-Completions Backends (...
by ga-it · 2026-02-07
77.0%
#14328: fix: strip incomplete tool_use blocks from errored/aborted messages...
by Kropiunig · 2026-02-12
76.6%
#19094: Fix empty tool_call_id and function names in provider transcript pa...
by yxshee · 2026-02-17
76.4%
#7085: test: skip flaky workspace-paths & safe-bins tests on non-Linux/CI ...
by ThinkIbrokeIt · 2026-02-02
76.0%
#6464: fix: trigger model failover on malformed tool-call JSON
by ai-fanatic · 2026-02-01
75.7%
#23803: Fix tool metadata truncation
by kamal-ayman · 2026-02-22
75.2%
#15050: fix: transcript corruption resilience — strip aborted tool_use bloc...
by yashchitneni · 2026-02-12
75.0%
#22516: fix: add resilient tool registration with per-tool error isolation
by white-rm · 2026-02-21
74.9%
#19235: fix(telegram): tool error warnings no longer overwrite streamed rep...
by gatewaybuddy · 2026-02-17
74.8%