fix: better logging on error (#152)
This commit is contained in:
@@ -680,11 +680,17 @@ export default function App({
|
||||
initialInput: Array<MessageCreate | ApprovalCreate>,
|
||||
): Promise<void> => {
|
||||
const currentInput = initialInput;
|
||||
// Track lastRunId outside the while loop so it's available in catch block
|
||||
let lastKnownRunId: string | null = null;
|
||||
|
||||
try {
|
||||
setStreaming(true);
|
||||
abortControllerRef.current = new AbortController();
|
||||
|
||||
// Clear any stale pending tool calls from previous turns
|
||||
// If we're sending a new message, old pending state is no longer relevant
|
||||
markIncompleteToolsAsCancelled(buffersRef.current);
|
||||
|
||||
while (true) {
|
||||
// Stream one turn
|
||||
const stream = await sendMessageStream(agentId, currentInput);
|
||||
@@ -696,6 +702,11 @@ export default function App({
|
||||
abortControllerRef.current?.signal,
|
||||
);
|
||||
|
||||
// Update lastKnownRunId for error handling in catch block
|
||||
if (lastRunId) {
|
||||
lastKnownRunId = lastRunId;
|
||||
}
|
||||
|
||||
// Track API duration
|
||||
sessionStatsRef.current.endTurn(apiDurationMs);
|
||||
sessionStatsRef.current.updateUsageFromBuffers(buffersRef.current);
|
||||
@@ -902,8 +913,13 @@ export default function App({
|
||||
// Mark incomplete tool calls as finished to prevent stuck blinking UI
|
||||
markIncompleteToolsAsCancelled(buffersRef.current);
|
||||
|
||||
// Build run info suffix for debugging
|
||||
const runInfoSuffix = lastRunId
|
||||
? `\n(run_id: ${lastRunId}, stop_reason: ${stopReason})`
|
||||
: `\n(stop_reason: ${stopReason})`;
|
||||
|
||||
// Fetch error details from the run if available
|
||||
let errorDetails = `Unexpected stop reason: ${stopReason}`;
|
||||
let errorDetails = `An error occurred during agent execution`;
|
||||
if (lastRunId) {
|
||||
try {
|
||||
const client = await getClient();
|
||||
@@ -924,31 +940,43 @@ export default function App({
|
||||
} catch (_e) {
|
||||
// If we can't fetch error details, let user know
|
||||
appendError(
|
||||
`${errorDetails}\n(Unable to fetch additional error details from server)`,
|
||||
`${errorDetails}${runInfoSuffix}\n(Unable to fetch additional error details from server)`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
appendError(errorDetails);
|
||||
appendError(`${errorDetails}${runInfoSuffix}`);
|
||||
|
||||
setStreaming(false);
|
||||
refreshDerived();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// Mark incomplete tool calls as cancelled to prevent stuck blinking UI
|
||||
markIncompleteToolsAsCancelled(buffersRef.current);
|
||||
|
||||
// Build error message with run_id for debugging
|
||||
const runIdSuffix = lastKnownRunId
|
||||
? `\n(run_id: ${lastKnownRunId}, stop_reason: error)`
|
||||
: "";
|
||||
|
||||
// Handle APIError from streaming (event: error)
|
||||
if (e instanceof APIError && e.error?.error) {
|
||||
const { type, message, detail } = e.error.error;
|
||||
const errorType = type ? `[${type}] ` : "";
|
||||
const errorMessage = message || "An error occurred";
|
||||
const errorDetail = detail ? `:\n${detail}` : "";
|
||||
appendError(`${errorType}${errorMessage}${errorDetail}`);
|
||||
appendError(
|
||||
`${errorType}${errorMessage}${errorDetail}${runIdSuffix}`,
|
||||
);
|
||||
} else {
|
||||
// Fallback for non-API errors
|
||||
appendError(e instanceof Error ? e.message : String(e));
|
||||
const errorMessage = e instanceof Error ? e.message : String(e);
|
||||
appendError(`${errorMessage}${runIdSuffix}`);
|
||||
}
|
||||
setStreaming(false);
|
||||
refreshDerived();
|
||||
} finally {
|
||||
abortControllerRef.current = null;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ import { createAgent } from "./agent/create";
|
||||
import { sendMessageStream } from "./agent/message";
|
||||
import { getModelUpdateArgs } from "./agent/model";
|
||||
import { SessionStats } from "./agent/stats";
|
||||
import { createBuffers, toLines } from "./cli/helpers/accumulator";
|
||||
import {
|
||||
createBuffers,
|
||||
markIncompleteToolsAsCancelled,
|
||||
toLines,
|
||||
} from "./cli/helpers/accumulator";
|
||||
import { safeJsonParseOr } from "./cli/helpers/safeJsonParse";
|
||||
import { drainStreamWithResume } from "./cli/helpers/stream";
|
||||
import { settingsManager } from "./settings-manager";
|
||||
@@ -424,6 +428,9 @@ export async function handleHeadlessCommand(
|
||||
},
|
||||
];
|
||||
|
||||
// Track lastRunId outside the while loop so it's available in catch block
|
||||
let lastKnownRunId: string | null = null;
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const stream = await sendMessageStream(agent.id, currentInput);
|
||||
@@ -643,6 +650,7 @@ export async function handleHeadlessCommand(
|
||||
apiDurationMs = performance.now() - startTime;
|
||||
// Use the last run_id we saw (if any)
|
||||
lastRunId = runIds.size > 0 ? Array.from(runIds).pop() || null : null;
|
||||
if (lastRunId) lastKnownRunId = lastRunId;
|
||||
|
||||
// Mark final line as finished
|
||||
const { markCurrentLineAsFinished } = await import(
|
||||
@@ -660,6 +668,7 @@ export async function handleHeadlessCommand(
|
||||
approvals = result.approvals || [];
|
||||
apiDurationMs = result.apiDurationMs;
|
||||
lastRunId = result.lastRunId || null;
|
||||
if (lastRunId) lastKnownRunId = lastRunId;
|
||||
}
|
||||
|
||||
// Track API duration for this stream
|
||||
@@ -772,6 +781,9 @@ export async function handleHeadlessCommand(
|
||||
}
|
||||
|
||||
// Unexpected stop reason (error, llm_api_error, etc.)
|
||||
// Mark incomplete tool calls as cancelled to prevent stuck state
|
||||
markIncompleteToolsAsCancelled(buffers);
|
||||
|
||||
// Extract error details from buffers if available
|
||||
const errorLines = toLines(buffers).filter(
|
||||
(line) => line.kind === "error",
|
||||
@@ -813,24 +825,39 @@ export async function handleHeadlessCommand(
|
||||
type: "error",
|
||||
message: errorMessage,
|
||||
stop_reason: stopReason,
|
||||
run_id: lastRunId,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
console.error(errorMessage);
|
||||
// Include run_id and stop_reason for debugging
|
||||
const runInfoSuffix = lastRunId
|
||||
? ` (run_id: ${lastRunId}, stop_reason: ${stopReason})`
|
||||
: ` (stop_reason: ${stopReason})`;
|
||||
console.error(`${errorMessage}${runInfoSuffix}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
// Mark incomplete tool calls as cancelled
|
||||
markIncompleteToolsAsCancelled(buffers);
|
||||
|
||||
// Build run info suffix for debugging
|
||||
const runInfoSuffix = lastKnownRunId
|
||||
? ` (run_id: ${lastKnownRunId}, stop_reason: error)`
|
||||
: "";
|
||||
|
||||
// Handle APIError from streaming (event: error)
|
||||
if (error instanceof APIError && error.error?.error) {
|
||||
const { type, message, detail } = error.error.error;
|
||||
const errorType = type ? `[${type}] ` : "";
|
||||
const errorMessage = message || "An error occurred";
|
||||
const errorDetail = detail ? `: ${detail}` : "";
|
||||
console.error(`Error: ${errorType}${errorMessage}${errorDetail}`);
|
||||
console.error(
|
||||
`Error: ${errorType}${errorMessage}${errorDetail}${runInfoSuffix}`,
|
||||
);
|
||||
} else {
|
||||
// Fallback for non-API errors
|
||||
console.error(`Error: ${error}`);
|
||||
console.error(`Error: ${error}${runInfoSuffix}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user