withastro/astro · error · Error
Process exited with code ${exitCode}
Error message
Process exited with code ${exitCode} What it means
Thrown by the `shell()` helper when the spawned child process exits with a non-zero `exitCode` AND both `stderr` and `stdout` are empty. It is the fallback message format string `Process exited with code ${exitCode}` used only when there is no captured output to report instead. When stderr/stdout is present, that output is thrown in its place, so hitting this exact message means the failure was silent.
Source
Thrown at packages/upgrade/src/shell.ts:75
cwd: opts.cwd,
stdio: opts.stdio,
timeout: opts.timeout,
});
const done = new Promise<void>((resolve, reject) => {
child.once('error', reject);
child.once('close', () => resolve());
});
[stdout, stderr] = await Promise.all([text(child.stdout), text(child.stderr), done]);
} catch (e) {
const message = e instanceof Error ? e.message : stderr || 'Unknown error';
throw new Error(message);
}
const { exitCode } = child;
if (exitCode === null) {
throw new Error('Timeout');
}
if (exitCode !== 0) {
throw new Error(stderr || stdout || `Process exited with code ${exitCode}`);
}
return { stdout, stderr, exitCode };
}
View on GitHub (pinned to d081033d5f)
Solutions
- Reproduce manually: run the same command and flags (`${command} ${flags.join(' ')}`) in the same `opts.cwd` to see the real failure output.
- Ensure `opts.stdio` is not set to `'ignore'`/`'pipe'` in a way that discards output — let stderr through for diagnostics.
- Check the exit code value to map it to the underlying tool's failure (e.g. git, npm/pnpm error codes).
- Confirm the executable exists and is on PATH (a missing binary often surfaces via the catch block, but a broken shim can exit silently).
Example fix
// before
if (exitCode !== 0) {
throw new Error(stderr || stdout || `Process exited with code ${exitCode}`);
}
// after — always include the command and exit code for debuggability
if (exitCode !== 0) {
throw new Error(
stderr || stdout || `${command} ${flags.join(' ')} exited with code ${exitCode}`,
);
} Defensive patterns
Strategy: try-catch
Validate before calling
import { which } from 'node:which'; // or shelljs
async function commandAvailable(cmd: string): Promise<boolean> {
try { await which(cmd); return true; } catch { return false; }
} Type guard
function isExitCodeError(e: unknown): boolean {
return e instanceof Error && /exited with code \d+/.test(e.message);
} Try / catch
try {
await shell(cmd, flags, { stdio: ['ignore','pipe','pipe'] });
} catch (e) {
// Always preserve stdout/stderr; do not set stdio to 'ignore'
if (isExitCodeError(e)) {
// re-run manually to capture output, or surface exitCode to the user
} else throw e;
} Prevention
- Never set `stdio: 'ignore'` on subprocesses whose failure you need to diagnose.
- Capture and log child stdout/stderr in your wrapper so silent failures still leave a trace.
- Verify the executable exists and is on PATH before spawning.
- Reproduce failing commands manually with the same cwd and flags.
When it happens
Trigger: A spawned command fails (non-zero exit) without writing anything to stdout or stderr — e.g. killed by a signal that Node translates to a non-zero exit but no output, a binary that exits via `_exit` without flushing, stdio streams closed/redirected away via `opts.stdio`, or a `.exe` shim on Windows that fails before producing output.
Common situations: A package manager or git command exits non-zero with output swallowed by a custom `stdio` config; a missing executable resolves but immediately fails; CI environments where stdio is piped/hidden.
Related errors
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/ba14133713f27437.
Report an issue: GitHub.