mastra-ai/mastra · error · ProtocolOutputError
${error.message}
Error message
${error.message} What it means
writeEvent queues protocol frames onto a serialized write tail; if stdout.write signals backpressure, it awaits waitForDrain(). Any failure there (including the deadline backpressure error) is rethrown as a ProtocolOutputError carrying the underlying message (this error entry's message is that inner message interpolated). ProtocolOutputError marks the output stream as broken, causing the worker run to fail rather than emit corrupt or truncated protocol output.
Source
Thrown at packages/cli/src/commands/experiment/runtime.ts:421
};
const frame = `${JSON.stringify(event)}\n`;
const frameBytes = Buffer.byteLength(frame);
if (frameBytes > EXPERIMENT_WORKER_MAX_FRAME_BYTES) {
return Promise.reject(new Error('output frame exceeds maximum size'));
}
if (pendingOutputBytes + frameBytes > EXPERIMENT_WORKER_MAX_PENDING_OUTPUT_BYTES) {
return Promise.reject(new Error('pending protocol output exceeds maximum size'));
}
sequence += 1;
pendingOutputBytes += frameBytes;
if (type === 'heartbeat') heartbeatQueued = true;
const queuedWrite = writeTail.then(async () => {
try {
if (!stdout.write(frame)) {
try {
await waitForDrain();
} catch (error) {
throw new ProtocolOutputError(error instanceof Error ? error.message : String(error), true);
}
}
} finally {
pendingOutputBytes -= frameBytes;
if (type === 'heartbeat') heartbeatQueued = false;
}
});
writeTail = queuedWrite.catch(error => {
if (type === 'heartbeat') abortForProtocolFailure(error instanceof Error ? error.message : String(error));
});
return queuedWrite;
};
const finish = async (
status: 'completed' | 'completed-with-errors' | 'failed' | 'cancelled' | 'timed-out',
semanticEvent: ExperimentEvent,
exitCode: number,
retryable = false,
) => {View on GitHub (pinned to 75dd419e61)
Solutions
- Make sure the process consuming the worker's stdout drains it continuously (see 948).
- Increase the experiment deadline or reduce event output size/frequency.
- Inspect the inner message in the ProtocolOutputError to identify whether it was backpressure-deadline or an I/O stream error.
- If caused by stream errors (e.g. EPIPE because the parent exited), keep the parent alive until the terminal frame is consumed.
Example fix
// before
const res = await runExperimentWorker({ mastra, runExperiment }); // parent exits early -> EPIPE
// after
const res = await runExperimentWorker({ mastra, runExperiment });
await childPromise; // keep consuming stdout until terminal frame before exiting Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: verify the stdout destination is writable and the consumer is attached
if (process.stdout.destroyed || (process.stdout.writableNeedDrain && process.stdout.writableLength > 1 << 20)) {
throw new Error('stdout consumer is not keeping up before experiment start');
} Type guard
function isProtocolOutputError(e: unknown): e is { name: 'ProtocolOutputError'; message: string; retryable: boolean } {
return typeof e === 'object' && e !== null && (e as any).name === 'ProtocolOutputError';
} Try / catch
try {
await runExperimentWorker({ mastra, runExperiment, build });
} catch (e) {
if (isProtocolOutputError(e)) {
console.error(`Worker output stream failed (${e.message}); inspect the stdout consumer and deadline`);
} else throw e;
} Prevention
- Keep the parent process reading stdout until the terminal frame arrives
- Increase the deadline or shrink output for output-heavy experiments
- Handle EPIPE in the parent (don't exit before the worker finishes)
- Monitor writableLength/writableNeedDrain as an early backpressure signal
When it happens
Trigger: A queued frame write fails during waitForDrain — deadline exceeded while stdout is blocked ("stdout backpressure exceeded the experiment deadline"), or the stream errors while waiting — and the message is wrapped in ProtocolOutputError.
Common situations: Long-running experiments whose consumer stalls; stdout piped into a file on a full disk; slow CI log ingestion combined with a strict experiment deadline; large emitted payloads saturating the pipe buffer.
Related errors
- tool_result must be preceded by a tool_call
- stdout backpressure exceeded the experiment deadline
- UI Messages require a data property when using data- prefixe
- UI Messages require a data property when using data- prefixe
- UI Messages require a data property when using data- prefixe
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/10433dafb342d8f6.
Report an issue: GitHub.