Bug type
Crash (process/app exits or hangs)
Summary
A plugin tool handler that returns undefined/void produces a malformed content block {type: "text"} (no text property), which crashes the context truncation pipeline on every subsequent message and locks the session into an unrecoverable crash loop.
Steps to reproduce
- Install or enable a plugin whose tool handler returns
undefined for at least one action (e.g. sentinel plugin's remove action has no return statement).
- Invoke that tool action in an agent session — for example, call
sentinel_control with action: "remove".
- The tool result is persisted to the session JSONL as
content: [{"type": "text"}] (missing text key).
- Send any follow-up message to the session.
Expected behavior
The session continues normally. If a tool handler returns undefined, the framework should either default the content to an empty string or reject the malformed block before persistence.
Actual behavior
Every subsequent message triggers:
TypeError: Cannot read properties of undefined (reading 'length')
at estimateMessageChars (reply-DhtejUNZ.js:76200:76)
The crash repeats on every incoming message because the malformed content block is already persisted in the session JSONL. The agent becomes completely unresponsive. In the observed incident, this lasted ~17 minutes until the session was manually reset.
Full crash path: enforceToolResultContextBudgetInPlace → truncateToolResultToChars → estimateMessageChars → isTextBlock returns true for {type: "text"} → block.text.length throws on undefined.
OpenClaw version
2026.3.2
Operating system
macOS
Install method
npm global
Logs, screenshots, and evidence
Session transcript timeline showing the corrupted tool result:
| Time (UTC) | Action | Result |
|---|---|---|
| 19:10:49 | list | Valid (`[]`) |
| 19:10:55 | add | Valid error (wrong strategy) |
| 19:11:18 | add | Valid error (host not allowed) |
| 19:11:42 | add | Valid error (host not allowed) |
| 19:12:52 | add | Success — created `timeapi-test-2361` |
| 19:13:06 | status | Success |
| 19:13:09 | get | Success |
| 19:13:12 | list | Success |
| 19:13:17 | remove | **Corrupted** — `{"type":"text"}` with no `text` |
The corrupted tool result message as persisted in the session JSONL:
{
"role": "toolResult",
"toolCallId": "call_iuCGHR5hjNegUaPHMjNIgeRL|fc_0f8c6651728332d20169a8844d498481929d39e79247a406d5",
"toolName": "sentinel_control",
"content": [{ "type": "text" }],
"isError": false
}
Every other `sentinel_control` action (`list`, `add`, `status`, `get`) returned well-formed `{"type": "text", "text": "..."}` blocks. Only `remove` produced the malformed response.
Impact and severity
- Affected users/systems/channels: Any session using a plugin whose tool handler returns
void/undefined for any action. The sentinel plugin's remove action is a known trigger, but any plugin with a missing return statement can cause this.
- Severity: Blocks workflow. The session becomes permanently unresponsive until manually reset. No data loss, but all in-flight conversation context is effectively lost.
- Frequency: Edge case per plugin action, but deterministic once triggered — every session that hits the codepath will crash, and every subsequent message in that session will crash again.
- Consequence: Complete session lockout. In the observed incident, the agent was unresponsive for ~17 minutes. Users have no self-service recovery path other than resetting the session.
Additional information
There are two independent bugs:
-
isTextBlock type guard (src/agents/pi-embedded-runner/tool-result-char-estimator.ts): Only checks block.type === "text" without verifying block.text is a string. This lets malformed blocks pass the guard, then block.text.length throws.
-
No content block validation at persistence (src/agents/session-tool-result-guard.ts): Malformed content blocks are persisted to session JSONL without sanitization. Once persisted, the crash triggers on every session load — there is no recovery without manual session reset or JSONL editing.
The plugin under development also needed a fix to properly return a string, but the core framework should be resilient to any plugin returning undefined.
Bug type
Crash (process/app exits or hangs)
Summary
A plugin tool handler that returns
undefined/voidproduces a malformed content block{type: "text"}(notextproperty), which crashes the context truncation pipeline on every subsequent message and locks the session into an unrecoverable crash loop.Steps to reproduce
undefinedfor at least one action (e.g. sentinel plugin'sremoveaction has noreturnstatement).sentinel_controlwithaction: "remove".content: [{"type": "text"}](missingtextkey).Expected behavior
The session continues normally. If a tool handler returns
undefined, the framework should either default the content to an empty string or reject the malformed block before persistence.Actual behavior
Every subsequent message triggers:
The crash repeats on every incoming message because the malformed content block is already persisted in the session JSONL. The agent becomes completely unresponsive. In the observed incident, this lasted ~17 minutes until the session was manually reset.
Full crash path:
enforceToolResultContextBudgetInPlace→truncateToolResultToChars→estimateMessageChars→isTextBlockreturnstruefor{type: "text"}→block.text.lengththrows onundefined.OpenClaw version
2026.3.2
Operating system
macOS
Install method
npm global
Logs, screenshots, and evidence
Impact and severity
void/undefinedfor any action. The sentinel plugin'sremoveaction is a known trigger, but any plugin with a missingreturnstatement can cause this.Additional information
There are two independent bugs:
isTextBlocktype guard (src/agents/pi-embedded-runner/tool-result-char-estimator.ts): Only checksblock.type === "text"without verifyingblock.textis a string. This lets malformed blocks pass the guard, thenblock.text.lengththrows.No content block validation at persistence (
src/agents/session-tool-result-guard.ts): Malformed content blocks are persisted to session JSONL without sanitization. Once persisted, the crash triggers on every session load — there is no recovery without manual session reset or JSONL editing.The plugin under development also needed a fix to properly return a string, but the core framework should be resilient to any plugin returning
undefined.