mastra-ai/mastra · warning · HTTPException

runId required to observe workflow stream

Error message

runId required to observe workflow stream

What it means

A 400 validation error thrown by the observe-workflow-stream handler when the runId parameter is missing. Observing a stream requires identifying which existing run's stream to attach to.

Source

Thrown at packages/server/src/server/handlers/workflows.ts:775

  responseType: 'stream',
  pathParamSchema: workflowIdPathParams,
  queryParamSchema: observeWorkflowQuerySchema,
  responseSchema: streamResponseSchema,
  summary: 'Observe workflow stream',
  description:
    'Observes and streams updates from an already running workflow execution. Supports position-based resume with offset for efficient reconnection.',
  tags: ['Workflows'],
  requiresAuth: true,
  handler: async ({ mastra, workflowId, runId, offset, requestContext }) => {
    try {
      const effectiveResourceId = getEffectiveResourceId(requestContext, undefined);

      if (!workflowId) {
        throw new HTTPException(400, { message: 'Workflow ID is required' });
      }

      if (!runId) {
        throw new HTTPException(400, { message: 'runId required to observe workflow stream' });
      }

      const { workflow } = await listWorkflowsFromSystem({ mastra, workflowId });

      if (!workflow) {
        throw new HTTPException(404, { message: 'Workflow not found' });
      }

      const run = await workflow.getWorkflowRunById(runId);

      if (!run) {
        throw new HTTPException(404, { message: 'Workflow run not found' });
      }

      await validateRunOwnership(run, effectiveResourceId);

      const _run = await workflow.createRun({ runId, resourceId: run.resourceId });
      const serverCache = mastra.getServerCache();

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Create or obtain a run first and pass its runId when observing the stream
  2. Persist the runId returned by createRun/start and reuse it
  3. Use the start-run route instead if you intended to launch a new run

Example fix

// before
await run.observeStream({}); // server route requires runId
// after
const run = await client.getWorkflow('wf').createRun();
await client.getWorkflow('wf').observeStream({ runId: run.runId });
Defensive patterns

Strategy: validation

Validate before calling

function assertRunId(id: string | undefined): asserts id is string {
  if (!id) throw new Error('runId is required — create or start a run first');
}

Type guard

function hasRunId(p: { runId?: string }): p is { runId: string } {
  return typeof p.runId === 'string' && p.runId.length > 0;
}

Try / catch

try {
  await observeStream({ workflowId, runId });
} catch (e: any) {
  if (e?.status === 400 && /runId required/.test(e?.message ?? '')) {
    const run = await client.getWorkflow(workflowId).createRun();
    // retry with run.runId
  } else throw e;
}

Prevention

When it happens

Trigger: POSTing to the observe-stream route without runId in body/query.

Common situations: Calling observe before creating/starting a run; client code losing the runId returned by createRun; confusing the observe route with the start route (which doesn't require runId).

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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