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

  1. Reproduce manually: run the same command and flags (`${command} ${flags.join(' ')}`) in the same `opts.cwd` to see the real failure output.
  2. Ensure `opts.stdio` is not set to `'ignore'`/`'pipe'` in a way that discards output — let stderr through for diagnostics.
  3. Check the exit code value to map it to the underlying tool's failure (e.g. git, npm/pnpm error codes).
  4. 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

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.