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
- Create or obtain a run first and pass its runId when observing the stream
- Persist the runId returned by createRun/start and reuse it
- 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
- Always capture the runId returned by createRun/start
- Distinguish start vs observe vs resume flows in client code
- Add unit tests that cover the observe path with a real runId
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
- Processor ID is required
- Phase is required
- Messages array is required
- bad request: ${responseText}
- Invalid agent-builder action: ${actionId}. Valid actions are
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/b9f3fc5bde10dc45.
Report an issue: GitHub.