Skip to content

Agent error handling

Genkit agent failures have different recovery paths depending on when they happen. A request can be rejected before an invocation starts, a turn can fail after state has been loaded, or a tool can return a domain-level result that the model can handle.

  • Init misuse throws before a turn starts. Fix the caller, such as by not sending state to a store-backed agent.
  • Failed turns throw AgentError with response, status, details, state, and snapshotId. Resume from the last-good state or snapshot.
  • Foreground aborts resolve with finishReason: 'aborted'. Let the user retry or revise the request.
  • Background failures appear as snapshot status failed, aborted, or expired. Show the status, inspect snapshot error details, and retry from a completed snapshot when possible.
  • Domain-level tool problems should be structured tool output when the model can recover. Let the model explain the issue or ask the user for corrected input.
import { AgentError } from 'genkit/beta/client';
try {
const res = await chat.send('Look up order 123.');
console.log(res.text);
} catch (err) {
if (err instanceof AgentError) {
console.error(err.status);
console.error(err.details);
console.error(err.snapshotId);
console.error(err.state);
const recoveryChat = err.snapshotId
? await agent.loadChat({ snapshotId: err.snapshotId })
: agent.chat({ state: err.response.raw.state });
await recoveryChat.send('Try again with order 456.');
} else {
throw err;
}
}

For streaming turns, catch errors around stream consumption and the final response. The stream rethrows failed-turn errors after yielding any available chunks.

const turn = chat.sendStream('Write a report.');
try {
for await (const chunk of turn.stream) {
render(chunk);
}
await turn.response;
} catch (err) {
showFailure(err);
}

Throw from a tool when the system cannot safely continue, such as a database outage, auth failure, or invariant violation.

const lookupOrder = ai.defineTool(
{
name: 'lookupOrder',
description: 'Looks up an order by ID.',
inputSchema: z.object({ orderId: z.string() }),
},
async ({ orderId }) => {
const order = await db.orders.find(orderId);
if (!order) {
throw new Error(`Order ${orderId} was not found.`);
}
return order;
},
);

Return structured data when the model can recover:

return {
ok: false,
reason: 'ORDER_NOT_FOUND',
message: 'Ask the user to check the order ID.',
};

Call res.assertValid() when a caller requires a model message and wants blocked responses to throw.

const res = await chat.send('Write the summary.');
res.assertValid();