fix: improve error handling for headless (#187)

This commit is contained in:
Charles Packer
2025-12-11 22:30:26 -08:00
committed by GitHub
parent 2bed9b071b
commit c39bda9a01

View File

@@ -1,5 +1,4 @@
import { parseArgs } from "node:util";
import { APIError } from "@letta-ai/letta-client/core/error";
import type {
AgentState,
MessageCreate,
@@ -19,6 +18,7 @@ import {
markIncompleteToolsAsCancelled,
toLines,
} from "./cli/helpers/accumulator";
import { formatErrorDetails } from "./cli/helpers/errorFormatter";
import { safeJsonParseOr } from "./cli/helpers/safeJsonParse";
import { drainStreamWithResume } from "./cli/helpers/stream";
import { settingsManager } from "./settings-manager";
@@ -846,20 +846,24 @@ export async function handleHeadlessCommand(
? errorMessages.join("; ")
: `Unexpected stop reason: ${stopReason}`;
// Fetch detailed error from run metadata if available
// Fetch detailed error from run metadata if available (same as TUI mode)
if (lastRunId && errorMessages.length === 0) {
try {
const run = await client.runs.retrieve(lastRunId);
if (run.metadata?.error) {
const error = run.metadata.error as {
const errorData = run.metadata.error as {
type?: string;
message?: string;
detail?: string;
};
const errorType = error.type ? `[${error.type}] ` : "";
const errorMsg = error.message || "An error occurred";
const errorDetail = error.detail ? `: ${error.detail}` : "";
errorMessage = `${errorType}${errorMsg}${errorDetail}`;
// Construct error object that formatErrorDetails can parse
const errorObject = {
error: {
error: errorData,
run_id: lastRunId,
},
};
errorMessage = formatErrorDetails(errorObject, agent.id);
}
} catch (_e) {
// If we can't fetch error details, append note to error message
@@ -878,11 +882,7 @@ export async function handleHeadlessCommand(
}),
);
} else {
// Include run_id and stop_reason for debugging
const runInfoSuffix = lastRunId
? ` (run_id: ${lastRunId}, stop_reason: ${stopReason})`
: ` (stop_reason: ${stopReason})`;
console.error(`${errorMessage}${runInfoSuffix}`);
console.error(`Error: ${errorMessage}`);
}
process.exit(1);
}
@@ -890,23 +890,19 @@ export async function handleHeadlessCommand(
// Mark incomplete tool calls as cancelled
markIncompleteToolsAsCancelled(buffers);
// Build run info suffix for debugging
const runInfoSuffix = lastKnownRunId
? ` (run_id: ${lastKnownRunId}, stop_reason: error)`
: "";
// Use comprehensive error formatting (same as TUI mode)
const errorDetails = formatErrorDetails(error, agent.id);
// 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}${runInfoSuffix}`,
if (outputFormat === "stream-json") {
console.log(
JSON.stringify({
type: "error",
message: errorDetails,
run_id: lastKnownRunId,
}),
);
} else {
// Fallback for non-API errors
console.error(`Error: ${error}${runInfoSuffix}`);
console.error(`Error: ${errorDetails}`);
}
process.exit(1);
}