From b9def0964e363e29e640cb3979a708f7b282a410 Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 22 Apr 2026 19:33:15 +0000 Subject: [PATCH] fix: improve partial-log handling and error messages for fast-exit containers (FAR-122) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add a second log fallback: if the follow stream captured partial output (init event present but no result event), attempt a one-shot readPodLogs before the pod is cleaned up. Fast-exiting containers (bad model, missing API key, etc.) can cause the follow stream to return only the init line before the connection drops; the one-shot read is more reliable for already-terminated containers. - Improve the `!parsed` error message: skip system/init events when searching for the first content line, so the error reads "Claude started but did not produce a result (model: MiniMax-M2.7) — check API credentials..." instead of "Claude exited with code -1: {"type":"system","subtype":"init",...}". Co-Authored-By: Paperclip --- src/server/execute.ts | 44 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/server/execute.ts b/src/server/execute.ts index c313f15..558a5b7 100644 --- a/src/server/execute.ts +++ b/src/server/execute.ts @@ -650,6 +650,19 @@ export async function execute(ctx: AdapterExecutionContext): Promise stdout.length) { + await onLog("stdout", `[paperclip] Log stream captured partial output — supplemental one-shot read returned more content.\n`); + stdout = fullLogs; + } + } + if (completionResult.status === "fulfilled") { jobTimedOut = completionResult.value.timedOut; if (completionResult.value.jobGone) { @@ -739,16 +752,39 @@ export async function execute(ctx: AdapterExecutionContext): Promise l.trim()).find(Boolean) ?? ""; + // Find the first stdout line that is NOT a system/init event. + // Using the system/init JSON blob as the error message produces a huge, + // unreadable error in the UI. Skip those and use the first real content line. + const firstContentLine = stdout.split(/\r?\n/) + .map((l) => l.trim()) + .find((l) => { + if (!l) return false; + try { + const obj = JSON.parse(l); + if (typeof obj === "object" && obj !== null && (obj as Record).type === "system") return false; + } catch { + // not JSON — treat as content + } + return true; + }) ?? ""; + + // If we got an init event but nothing else, give a specific message that + // names the model so it is easier to diagnose (e.g. unsupported model, + // missing API credentials). + const initOnlyOutput = stdout.trim() !== "" && parsedStream.model !== "" && !firstContentLine; + const modelHint = parsedStream.model ? ` (model: ${parsedStream.model})` : ""; + return { exitCode, signal: null, timedOut: false, errorMessage: exitCode === 0 ? "Failed to parse Claude JSON output" - : stderrLine - ? `Claude exited with code ${exitCode ?? -1}: ${stderrLine}` - : `Claude exited with code ${exitCode ?? -1}`, + : initOnlyOutput + ? `Claude started but did not produce a result${modelHint} — check API credentials, model support, and adapter config` + : firstContentLine + ? `Claude exited with code ${exitCode ?? -1}: ${firstContentLine}` + : `Claude exited with code ${exitCode ?? -1}`, resultJson: { stdout }, }; }