From fb17a322832edbfa7c7477279f4c949c0176ab34 Mon Sep 17 00:00:00 2001 From: Emanuel Stadler <9994339+emanuelst@users.noreply.github.com> Date: Tue, 6 Jan 2026 21:17:55 +0100 Subject: [PATCH 1/3] feat: enhance error handling for socket connection errors - Added `isError` property to `EmbeddedPiRunResult` and reply items to indicate error states. - Updated error handling in `runReplyAgent` to provide more informative messages for specific socket connection errors. --- src/agents/pi-embedded-runner.ts | 11 +++++++++-- src/auto-reply/reply/agent-runner.ts | 21 +++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/agents/pi-embedded-runner.ts b/src/agents/pi-embedded-runner.ts index 813a4ba0d..78e895c24 100644 --- a/src/agents/pi-embedded-runner.ts +++ b/src/agents/pi-embedded-runner.ts @@ -99,6 +99,7 @@ export type EmbeddedPiRunResult = { mediaUrl?: string; mediaUrls?: string[]; replyToId?: string; + isError?: boolean; }>; meta: EmbeddedPiRunMeta; }; @@ -1009,12 +1010,17 @@ export async function runEmbeddedPiAgent(params: { usage, }; - const replyItems: Array<{ text: string; media?: string[] }> = []; + const replyItems: Array<{ + text: string; + media?: string[]; + isError?: boolean; + }> = []; const errorText = lastAssistant ? formatAssistantErrorText(lastAssistant) : undefined; - if (errorText) replyItems.push({ text: errorText }); + + if (errorText) replyItems.push({ text: errorText, isError: true }); const inlineToolResults = params.verboseLevel === "on" && @@ -1047,6 +1053,7 @@ export async function runEmbeddedPiAgent(params: { text: item.text?.trim() ? item.text.trim() : undefined, mediaUrls: item.media?.length ? item.media : undefined, mediaUrl: item.media?.[0], + isError: item.isError, })) .filter( (p) => diff --git a/src/auto-reply/reply/agent-runner.ts b/src/auto-reply/reply/agent-runner.ts index d4e7ba652..d25aac8e6 100644 --- a/src/auto-reply/reply/agent-runner.ts +++ b/src/auto-reply/reply/agent-runner.ts @@ -401,8 +401,25 @@ export async function runReplyAgent(params: { const sanitizedPayloads = isHeartbeat ? payloadArray : payloadArray.flatMap((payload) => { - const text = payload.text; - if (!text || !text.includes("HEARTBEAT_OK")) return [payload]; + let text = payload.text; + + if (payload.isError) { + // Handle Bun fetch socket connection error that may indicate a context length issue + // Error source: https://github.com/oven-sh/bun/blob/main/src/bun.js/webcore/fetch/FetchTasklet.zig + const isBunFetchSocketError = + text === + "The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()"; + + if (isBunFetchSocketError) { + text = `⚠️ LLM connection failed. This could be due to server issues, network problems, or context length exceeded (e.g., with local LLMs like LM Studio). Original error: + \`\`\` + ${text || "Unknown error"} + \`\`\``; + } + } + + if (!text || !text.includes("HEARTBEAT_OK")) + return [{ ...payload, text }]; const stripped = stripHeartbeatToken(text, { mode: "message" }); if (stripped.didStrip && !didLogHeartbeatStrip) { didLogHeartbeatStrip = true; From a1f5cfcd08c8bc0439e1f7e70e227f8521070fbc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 6 Jan 2026 22:26:42 +0100 Subject: [PATCH 2/3] docs: refresh clawtributors --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 35500d4b2..848666e45 100644 --- a/README.md +++ b/README.md @@ -453,4 +453,5 @@ Thanks to all clawtributors: azade-c andranik-sahakyan adamgall jalehman jarvis-medmatic mneves75 regenrek tobiasbischoff MSch obviyus dbhurley Asleep123 Iamadig imfing kitze nachoiacovino VACInc cash-echo-bot claude kiranjd pcty-nextgen-service-account minghinmatthewlam + ngutman onutc oswalpalash snopoke ManuelHettich loukotal hugobarauna AbhisekBasu1

From 96164b5955c913657e9730ffddd6a4eb51919471 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 6 Jan 2026 22:43:29 +0100 Subject: [PATCH 3/3] fix: improve socket error handling --- CHANGELOG.md | 1 + .../agent-runner.heartbeat-typing.test.ts | 22 ++++++++++++++ src/auto-reply/reply/agent-runner.ts | 30 +++++++++++-------- src/auto-reply/types.ts | 1 + 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c9e7b3e9..17c9080de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - Telegram: stop typing after tool results. Thanks @AbhisekBasu1 for PR #322. - Messages: stop defaulting ack reactions to 👀 when identity emoji is missing. - Auto-reply: require slash for control commands to avoid false triggers in normal text. +- Auto-reply: flag error payloads and improve Bun socket error messaging. Thanks @emanuelst for PR #331. - Commands: unify native + text chat commands behind `commands.*` config (Discord/Slack/Telegram). Thanks @thewilloftheshadow for PR #275. - Auto-reply: treat steer during compaction as a follow-up, queued until compaction completes. - Auth: lock auth profile refreshes to avoid multi-instance OAuth logouts; keep credentials on refresh failure. diff --git a/src/auto-reply/reply/agent-runner.heartbeat-typing.test.ts b/src/auto-reply/reply/agent-runner.heartbeat-typing.test.ts index 85eb5cf84..017a51ca9 100644 --- a/src/auto-reply/reply/agent-runner.heartbeat-typing.test.ts +++ b/src/auto-reply/reply/agent-runner.heartbeat-typing.test.ts @@ -209,4 +209,26 @@ describe("runReplyAgent typing (heartbeat)", () => { expect(payloads[0]?.text).toContain("count 1"); expect(sessionStore.main.compactionCount).toBe(1); }); + + it("rewrites Bun socket errors into friendly text", async () => { + runEmbeddedPiAgentMock.mockImplementationOnce(async () => ({ + payloads: [ + { + text: "TypeError: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()", + isError: true, + }, + ], + meta: {}, + })); + + const { run } = createMinimalRun(); + const res = await run(); + const payloads = Array.isArray(res) ? res : res ? [res] : []; + expect(payloads.length).toBe(1); + expect(payloads[0]?.text).toContain("LLM connection failed"); + expect(payloads[0]?.text).toContain( + "socket connection was closed unexpectedly", + ); + expect(payloads[0]?.text).toContain("```"); + }); }); diff --git a/src/auto-reply/reply/agent-runner.ts b/src/auto-reply/reply/agent-runner.ts index d25aac8e6..79b85e57a 100644 --- a/src/auto-reply/reply/agent-runner.ts +++ b/src/auto-reply/reply/agent-runner.ts @@ -31,6 +31,21 @@ import { extractReplyToTag } from "./reply-tags.js"; import { incrementCompactionCount } from "./session-updates.js"; import type { TypingController } from "./typing.js"; +const BUN_FETCH_SOCKET_ERROR_RE = /socket connection was closed unexpectedly/i; + +const isBunFetchSocketError = (message?: string) => + Boolean(message && BUN_FETCH_SOCKET_ERROR_RE.test(message)); + +const formatBunFetchSocketError = (message: string) => { + const trimmed = message.trim(); + return [ + "⚠️ LLM connection failed. This could be due to server issues, network problems, or context length exceeded (e.g., with local LLMs like LM Studio). Original error:", + "```", + trimmed || "Unknown error", + "```", + ].join("\n"); +}; + export async function runReplyAgent(params: { commandBody: string; followupRun: FollowupRun; @@ -403,19 +418,8 @@ export async function runReplyAgent(params: { : payloadArray.flatMap((payload) => { let text = payload.text; - if (payload.isError) { - // Handle Bun fetch socket connection error that may indicate a context length issue - // Error source: https://github.com/oven-sh/bun/blob/main/src/bun.js/webcore/fetch/FetchTasklet.zig - const isBunFetchSocketError = - text === - "The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()"; - - if (isBunFetchSocketError) { - text = `⚠️ LLM connection failed. This could be due to server issues, network problems, or context length exceeded (e.g., with local LLMs like LM Studio). Original error: - \`\`\` - ${text || "Unknown error"} - \`\`\``; - } + if (payload.isError && text && isBunFetchSocketError(text)) { + text = formatBunFetchSocketError(text); } if (!text || !text.includes("HEARTBEAT_OK")) diff --git a/src/auto-reply/types.ts b/src/auto-reply/types.ts index 62b6d75bb..b76a0a5a1 100644 --- a/src/auto-reply/types.ts +++ b/src/auto-reply/types.ts @@ -14,4 +14,5 @@ export type ReplyPayload = { mediaUrl?: string; mediaUrls?: string[]; replyToId?: string; + isError?: boolean; };