denoland/deno · error · TypeError
Cannot collect output: 'stderr' is locked
Error message
Cannot collect output: 'stderr' is locked
What it means
ChildProcess.output() internally calls collectOutput() on child.stderr, which needs to acquire its own reader. If a prior getReader(), pipeTo(), or pipedThrough() on child.stderr locked the stream, output() throws a TypeError immediately. stderr and stdout are checked independently, and the stdout check runs first.
Source
Thrown at ext/process/40_process.js:540
signal?.[abortSignal.remove](onAbort);
this.#waitComplete = true;
return res;
});
}
#status;
get status() {
return this.#status;
}
async output() {
if (this.#stdout?.locked) {
throw new TypeError(
"Cannot collect output: 'stdout' is locked",
);
}
if (this.#stderr?.locked) {
throw new TypeError(
"Cannot collect output: 'stderr' is locked",
);
}
const { 0: status, 1: stdout, 2: stderr } = await SafePromiseAll([
this.#status,
collectOutput(this.#stdout),
collectOutput(this.#stderr),
]);
return {
success: status.success,
code: status.code,
signal: status.signal,
get stdout() {
if (stdout == null) {
throw new TypeError("Cannot get 'stdout': 'stdout' is not piped");
}View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Choose one consumption model: full capture via output(), or manual reader on .stderr (and .stdout) without calling output()
- Release the mistaken lock with reader.releaseLock() or cancel() before calling output()
- If you need live stderr plus the final combined output, read both streams manually and build the result yourself
Example fix
// before const child = cmd.spawn(); child.stderr.pipeTo(logSink); const out = await child.output(); // TypeError: 'stderr' is locked // after const child = cmd.spawn(); const out = await child.output(); console.errorText = new TextDecoder().decode(out.stderr);
Defensive patterns
Strategy: type-guard
Validate before calling
function canCollectOutput(child: Deno.ChildProcess): boolean {
return !(child.stdout?.locked ?? false) && !(child.stderr?.locked ?? false);
} Type guard
const outputSafe = (child: Deno.ChildProcess): boolean => child.stdout?.locked !== true && child.stderr?.locked !== true;
Try / catch
try { out = await child.output(); } catch (err) { if (err instanceof TypeError && err.message.includes("'stderr' is locked")) { /* consume streams manually instead */ } else throw err; } Prevention
- Do not attach pipes/readers to stderr unless you intend to finish the capture yourself
- Keep the locking rule in mind: any getReader/pipeTo/pipedThrough locks the stream
- Structure code so the component that locks the stream also fully drains it
When it happens
Trigger: Acquiring a reader on child.stderr (e.g. to stream error output live) and subsequently awaiting child.output(); attaching a pipe to stderr for logging, then calling output() for the final result.
Common situations: Streaming stderr to a console while also wanting the captured result; error-handling wrappers that read .stderr before delegating to output(); mixing manual reads for one stream with output() for both.
Related errors
- Cannot collect output: 'stdout' is locked
- Cannot get 'stderr': 'stderr' is not piped
- Piped stdin is not supported for this function, use 'Deno.Co
- ERR_STREAM_NULL_VALUES
- Cannot get 'stdout': 'stdout' is not piped
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/22aaee22e004b85b.
Report an issue: GitHub.