mastra-ai/mastra · error · MastraNonRetryableError

res.error.message

Error message

res.error.message

What it means

After a workflow run finishes with status 'failed', start() rethrows the run's error to the caller. If any failed step was marked nonRetryable, the error is wrapped in MastraNonRetryableError; otherwise the original `res.error` is thrown. The observed message is whatever the underlying run error was.

Source

Thrown at packages/core/src/workflows/workflow.ts:2934

              ...(propagatedForeachOutput ? { foreachOutput: propagatedForeachOutput } : {}),
              runId: run.runId,
              path: suspendPath,
            },
          },
          {
            resumeLabel: Object.keys(res.resumeLabels ?? {}),
          },
        );
      }
    }

    if (res.status === 'failed') {
      const isNonRetryable = Object.values(res.steps).some(stepResult => {
        const result = stepResult as StepResult<any, any, any, any>;
        return result.status === 'failed' && result.nonRetryable;
      });
      if (isNonRetryable) {
        throw new MastraNonRetryableError(res.error.message, { cause: res.error });
      }
      throw res.error;
    }

    if (res.status === 'tripwire') {
      const tripwire = res.tripwire;
      throw new TripWire(
        tripwire?.reason || 'Processor tripwire triggered',
        {
          retry: tripwire?.retry,
          metadata: tripwire?.metadata,
        },
        tripwire?.processorId,
      );
    }

    return res.status === 'success' ? res.result : undefined;
  }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Inspect `res.error` (or the caught error's cause) to find the failing step and root message.
  2. Catch the error around `await run.start()` and handle per-step failures via `run.steps` status/details.
  3. If the failure is legitimately transient, allow retries (don't mark it nonRetryable) and re-run.
  4. Fix the failing step's logic or input that produced the underlying error.

Example fix

// before
await run.start({ inputData });
// after
const res = await run.start({ inputData });
if (res.status === 'failed') {
  console.error('run failed:', res.error, Object.entries(res.steps).filter(([, s]) => s.status === 'failed'));
}
Defensive patterns

Strategy: try-catch

Type guard

const isFailedRun = (res: { status: string; error?: unknown; steps?: Record<string, { status: string }> }): boolean =>
  res.status === 'failed';

Try / catch

const res = await run.start({ inputData });
if (res.status === 'failed') {
  const failing = Object.entries(res.steps ?? {}).filter(([, s]) => s.status === 'failed');
  console.error(res.error, failing);
  // handle or rethrow
}

Prevention

When it happens

Trigger: Awaiting a run (via `run.start()` or watch/unwatch callbacks) whose result status is 'failed'; the thrown value is `res.error` from the failed run — inspect its cause/message for the true failure.

Common situations: Unhandled step failure inside a workflow (schema mismatch, tool exception, nested workflow failure); retry budget exhausted; calling code without try/catch around run.start().

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/8dc58c1dc68817c4. Report an issue: GitHub.