#19024: fix: Fix normalise toolid
agents
size: S
Cluster:
Error Handling in Agent Tools
Summary
Problem: Session transcripts can contain tool call IDs like functions.exec:0 (and sometimes leading whitespace), which can violate strict tool-id validation on reload.
Why it matters: Can make sessions fail to load / appear “corrupted”, breaking recovery and usability.
What changed: Normalize tool call IDs at persistence time to safe ^[a-zA-Z0-9_-]+$ IDs and apply a stable mapping so toolResults remain paired.
What did NOT change (scope boundary): No change to tool execution logic, provider APIs, or runtime tool-calling behavior—only transcript persistence normalization.
Change Type (select all)
Bug fix
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 https://github.com/openclaw/openclaw/issues/18484
User-visible / Behavior Changes
Session transcripts persist tool call IDs in a normalized safe format; sessions should reload reliably even if upstream tool IDs contain : / . / whitespace.
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: ubuntu 22.04
Runtime/container: local
Model/provider: OpenAI-compatible APIs that emit tool ids like functions.exec:0
Integration/channel (if any): N/A
Relevant config (redacted): N/A
Steps
Run a tool-calling turn where the provider emits a tool call id like " functions.exec:0".
Persist to session transcript.
Reload/parse session transcript.
Expected
Session loads successfully; toolResult entries remain paired with their toolCalls.
Actual (before)
Session parser/validator can reject tool IDs that don’t match strict patterns, causing reload failure / session corruption.
Evidence
Failing test/log before + passing after
Added e2e test: src/agents/session-tool-result-guard.e2e.test.ts (“normalizes invalid tool call IDs…”)
Ran: vitest -c vitest.e2e.config.ts run src/agents/session-tool-result-guard.e2e.test.ts
Human Verification (required)
Verified scenarios:
Persisting an assistant toolCall with id " functions.exec:0" results in a normalized persisted id matching ^[a-zA-Z0-9_-]+$.
Matching toolResult toolCallId remains paired to the normalized toolCall id.
Edge cases checked:
Whitespace trimming; collision handling via stable mapping; idempotence (avoid re-mapping already-normalized ids).
What you did not verify:
End-to-end reproduction with a live OpenAI-compatible provider (manual session reload) beyond the e2e test harness.
Compatibility / Migration
Backward compatible? Yes
Config/env changes? No
Migration needed? No
If yes, exact upgrade steps: N/A
Greptile Summary
Adds ID normalization for session transcript persistence to prevent reload failures when tool call IDs contain invalid characters like :, ., or whitespace. The fix applies a stable mapping that normalizes tool call IDs before persistence and ensures toolResult entries remain paired with their corresponding toolCall entries.
Normalizes tool call IDs to match ^[a-zA-Z0-9_-]+$ pattern before persisting to session transcripts
Uses stable mapping (persistedIdMap) to ensure toolResult messages remain paired with normalized toolCall IDs
Handles collision detection via usedPersistedIds set with fallback strategies (hash suffix, numeric suffix, timestamp-based)
Includes comprehensive test coverage for the new normalization logic
Previous reviewer feedback addressed: removed duplicate shortHash function and eliminated double ID mapping in persistToolResult
Confidence Score: 5/5
Safe to merge - well-tested bug fix with no breaking changes
The fix correctly addresses a specific session reload failure by normalizing tool call IDs at persistence time. The implementation includes proper collision handling, idempotence checks, and maintains backward compatibility. Previous reviewer feedback has been addressed (duplicate shortHash removed, double mapping eliminated). Test coverage validates the normalization logic and pairing behavior.
No files require special attention
Last reviewed commit: https://github.com/openclaw/openclaw/commit/f20736a3a59916e6620bcaf9b8b3c46e2d81c6c9
Greptile Summary
Fixes session reload failures caused by tool call IDs containing invalid characters (:, ., whitespace) from OpenAI-compatible APIs. The implementation normalizes tool call IDs to match ^[a-zA-Z0-9_-]+$ before persisting to session transcripts while maintaining proper pairing between toolCall and toolResult messages through a stable ID mapping.
Adds normalizePersistedToolCallId function that strips invalid characters and collapses underscores
Implements resolvePersistedId with collision handling using hash suffixes, numeric suffixes, and timestamp-based fallbacks
Applies normalization to both assistant messages (toolCall/toolUse/functionCall blocks) and toolResult messages (toolCallId/toolUseId fields)
Includes comprehensive test coverage for the normalization logic
Test file changes add default suppressToolErrorWarnings: false value to heartbeat test expectations (unrelated fix)
Confidence Score: 5/5
Safe to merge - well-tested bug fix with no breaking changes
The implementation correctly addresses session reload failures through ID normalization at persistence time. The logic includes proper collision handling with multiple fallback strategies, maintains backward compatibility, and has comprehensive test coverage. The PR description indicates previous reviewer feedback was addressed (duplicate shortHash removed, double mapping eliminated). The heartbeat test changes are unrelated but benign test fixes.
No files require special attention
Last reviewed commit: https://github.com/openclaw/openclaw/commit/c4a4777f31420418194467348d2aa4dc20665cd8
(3/5) Reply to the agent's comments like "Can you suggest a fix for this https://github.com/greptileai?" or ask follow-up questions!
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds ID normalization for session transcript persistence to prevent reload failures when tool call IDs from OpenAI-compatible APIs contain invalid characters (`:`, `.`, whitespace). The implementation applies a stable mapping that normalizes tool call IDs before persistence while maintaining proper pairing between `toolCall` and `toolResult` entries.
- Normalizes tool call IDs to match `^[a-zA-Z0-9_-]+$` pattern before persisting
- Uses stable ID mapping (`persistedIdMap`, `usedPersistedIds`) to ensure `toolResult` messages remain paired with normalized `toolCall` IDs
- Implements collision handling via hash suffix, numeric suffix, and timestamp-based fallbacks
- Includes comprehensive test coverage validating normalization logic and pairing behavior
- One critical logical error found in spread operator usage (line 247-248) that will cause runtime errors
<h3>Confidence Score: 3/5</h3>
- This PR contains a critical logical error that will cause runtime failures
- The implementation correctly addresses session reload failures through ID normalization, but contains a critical bug at lines 247-248 where spreading `null` will throw a runtime error. This bug affects the core functionality of persisting toolResult messages with normalized IDs
- src/agents/session-tool-result-guard.ts requires immediate fix for the spread operator bug at lines 247-248
<sub>Last reviewed commit: 4ec4beb</sub>
<!-- greptile_other_comments_section -->
<sub>(5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#19094: Fix empty tool_call_id and function names in provider transcript pa...
by yxshee · 2026-02-17
86.8%
#3647: fix: sanitize tool arguments in session history
by nhangen · 2026-01-29
83.6%
#15050: fix: transcript corruption resilience — strip aborted tool_use bloc...
by yashchitneni · 2026-02-12
83.0%
#14328: fix: strip incomplete tool_use blocks from errored/aborted messages...
by Kropiunig · 2026-02-12
82.7%
#9011: fix(session): auto-recovery for corrupted tool responses [AI-assisted]
by cheenu1092-oss · 2026-02-04
82.3%
#12487: fix(agents): strip orphaned tool_result when tool_use is sanitized ...
by skylarkoo7 · 2026-02-09
82.1%
#15649: fix: sanitize tool_use IDs on session write path
by aldoeliacim · 2026-02-13
82.0%
#21195: fix: suppress orphaned tool_use/tool_result errors after session co...
by ruslansychov-git · 2026-02-19
81.9%
#21166: fix(agents): sanitize tool names in session transcript repair (#8595)
by dinakars777 · 2026-02-19
81.5%
#12812: fix(transcript-policy): sanitize tool call IDs for all non-OpenAI p...
by justin-nevins · 2026-02-09
81.5%