> 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)