UNPKG

@mastra/core

Version:
362 lines (280 loc) 9.87 kB
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt # Error handling Mastra workflows support error handling through result status checks after execution, retry policies for transient failures, and lifecycle callbacks for centralized error logging or alerting. ## Handling workflow results When you run a workflow, the result object contains the status and any errors that occurred. ### Checking the result status ```typescript import { mastra } from './mastra' const workflow = mastra.getWorkflow('myWorkflow') const run = await workflow.createRun() const result = await run.start({ inputData: { value: 'test' } }) switch (result.status) { case 'success': console.log('Workflow completed:', result.result) break case 'failed': console.error('Workflow failed:', result.error) break case 'suspended': console.log('Workflow suspended, waiting for resume') break } ``` ### Result object structure The result object contains: - `status` - The workflow status: `'success'`, `'failed'`, `'suspended'`, or `'tripwire'` - `result` - The workflow output (when status is `'success'`) - `error` - Error details (when status is `'failed'`) - `steps` - Individual step results with their status and output ### Accessing step results You can inspect individual step results to understand where a failure occurred: ```typescript const result = await run.start({ inputData: { value: 'test' } }) if (result.status === 'failed') { // Find which step failed for (const [stepId, stepResult] of Object.entries(result.steps)) { if (stepResult.status === 'failed') { console.error(`Step ${stepId} failed:`, stepResult.error) } } } ``` ## Lifecycle callbacks For scenarios where you need to handle workflow completion without awaiting the result, such as background jobs, fire-and-forget workflows, or centralized logging, you can use lifecycle callbacks. ### `onFinish` Called when a workflow completes with any status (success, failed, suspended, or tripwire): ```typescript import { createWorkflow } from '@mastra/core/workflows' import { z } from 'zod' const orderWorkflow = createWorkflow({ id: 'order-processing', inputSchema: z.object({ orderId: z.string() }), outputSchema: z.object({ orderId: z.string(), status: z.string() }), options: { onFinish: async result => { if (result.status === 'success') { await db.updateOrderStatus(result.result.orderId, result.status) } await analytics.track('workflow_completed', { workflowId: 'order-processing', status: result.status, }) }, }, }) ``` The `onFinish` callback receives: - `status` - The workflow status - `result` - The workflow output (on success) - `error` - Error details (on failure) - `steps` - Individual step results - `tripwire` - Tripwire info (if status is `'tripwire'`) - `runId` - The unique identifier for this workflow run - `workflowId` - The workflow's identifier - `resourceId` - Optional resource identifier (if provided when creating the run) - `getInitData<any>()` - Function that returns the initial input data - `mastra` - The Mastra instance (if workflow is registered with Mastra) - `requestContext` - Request-scoped context data - `logger` - The workflow's logger instance - `state` - The workflow's current state object ### `onError` Called only when a workflow fails (status is `'failed'` or `'tripwire'`): ```typescript import { createWorkflow } from '@mastra/core/workflows' import { z } from 'zod' const paymentWorkflow = createWorkflow({ id: 'payment-processing', inputSchema: z.object({ amount: z.number() }), outputSchema: z.object({ transactionId: z.string() }), options: { onError: async errorInfo => { await alertService.notify({ channel: 'payments-alerts', message: `Payment workflow failed: ${errorInfo.error?.message}`, }) await errorTracker.capture(errorInfo.error) }, }, }) ``` The `onError` callback receives: - `status` - Either `'failed'` or `'tripwire'` - `error` - Error details - `steps` - Individual step results - `tripwire` - Tripwire info (if status is `'tripwire'`) - `runId` - The unique identifier for this workflow run - `workflowId` - The workflow's identifier - `resourceId` - Optional resource identifier (if provided when creating the run) - `getInitData<any>()` - Function that returns the initial input data - `mastra` - The Mastra instance (if workflow is registered with Mastra) - `requestContext` - Request-scoped context data - `logger` - The workflow's logger instance - `state` - The workflow's current state object ### Using both callbacks You can use both callbacks together: ```typescript import { createWorkflow } from '@mastra/core/workflows' import { z } from 'zod' const pipelineWorkflow = createWorkflow({ id: 'data-pipeline', inputSchema: z.object({ source: z.string() }), outputSchema: z.object({ recordsProcessed: z.number() }), options: { onFinish: async result => { // Always log completion await logger.info('Pipeline completed', { status: result.status }) }, onError: async errorInfo => { // Alert on failures await pagerDuty.alert('Data pipeline failed', errorInfo.error) }, }, }) ``` ### Error handling in callbacks Errors thrown inside callbacks are caught and logged, they won't affect the workflow result or cause it to fail. As a result, callback issues don't break your workflows in production. ```typescript options: { onFinish: async (result) => { // If this throws, it's logged but the workflow result is unchanged await externalService.notify(result); }, } ``` ## Retries Mastra has a retry mechanism for workflows or steps that fail due to transient errors, for example when steps interact with external services or resources that may be temporarily unavailable. ## Workflow-level using `retryConfig` You can configure retries at the workflow level, which applies to all steps in the workflow: ```typescript import { createWorkflow, createStep } from "@mastra/core/workflows"; import { z } from "zod"; const step1 = createStep({...}); export const testWorkflow = createWorkflow({ retryConfig: { attempts: 5, delay: 2000 } }) .then(step1) .commit(); ``` ## Step-level using `retries` You can configure retries for individual steps using the `retries` property. This overrides the workflow-level retry configuration for that specific step: ```typescript import { createWorkflow, createStep } from '@mastra/core/workflows' import { z } from 'zod' const step1 = createStep({ execute: async () => { const response = await fetch('example-url') if (!response.ok) { throw new Error('Error') } return { value: '', } }, retries: 3, }) ``` ## Conditional branching You can create alternative workflow paths based on the success or failure of previous steps using conditional logic: ```typescript import { createWorkflow, createStep } from "@mastra/core/workflows"; import { z } from "zod"; const step1 = createStep({ execute: async () => { try { const response = await fetch('example-url'); if (!response.ok) { throw new Error('error'); } return { status: "ok" }; } catch (error) { return { status: "error" }; } } }); const step2 = createStep({...}); const fallback = createStep({...}); export const testWorkflow = createWorkflow({}) .then(step1) .branch([ [async ({ inputData: { status } }) => status === "ok", step2], [async ({ inputData: { status } }) => status === "error", fallback] ]) .commit(); ``` ## Check previous step results Use `getStepResult()` to inspect a previous step’s results. ```typescript import { createStep } from "@mastra/core/workflows"; import { z } from "zod"; const step1 = createStep({...}); const step2 = createStep({ execute: async ({ getStepResult }) => { const step1Result = getStepResult(step1); return { value: "" }; } }); ``` ## Exiting early with `bail()` Use `bail()` in a step to exit early with a successful result. This returns the provided payload as the step output and ends workflow execution. ```typescript import { createWorkflow, createStep } from "@mastra/core/workflows"; import { z } from "zod"; const step1 = createStep({ id: 'step1', execute: async ({ bail }) => { return bail({ result: 'bailed' }); }, inputSchema: z.object({ value: z.string() }), outputSchema: z.object({ result: z.string() }), }); export const testWorkflow = createWorkflow({...}) .then(step1) .commit(); ``` ## Exiting early with `Error()` Use `throw new Error()` in a step to exit with an error. ```typescript import { createWorkflow, createStep } from "@mastra/core/workflows"; import { z } from "zod"; const step1 = createStep({ id: 'step1', execute: async () => { throw new Error('error'); }, inputSchema: z.object({ value: z.string() }), outputSchema: z.object({ result: z.string() }), }); export const testWorkflow = createWorkflow({...}) .then(step1) .commit(); ``` \## Monitor errors with `stream()` You can monitor workflows for errors using `stream`: ```typescript import { mastra } from '../src/mastra' const workflow = mastra.getWorkflow('testWorkflow') const run = await workflow.createRun() const stream = await run.stream({ inputData: { value: 'initial data', }, }) for await (const chunk of stream.stream) { console.log(chunk.payload.output.stats) } ``` ## Related - [Control Flow](https://mastra.ai/docs/workflows/control-flow) - [Suspend and Resume](https://mastra.ai/docs/workflows/suspend-and-resume) - [Time Travel](https://mastra.ai/docs/workflows/time-travel) - [Human-in-the-loop](https://mastra.ai/docs/workflows/human-in-the-loop)