can1357/oh-my-pi · error · NonZeroExitError

Process exited with code ${exitCode}: ${stderr}

Error message

Process exited with code ${exitCode}:
${stderr}

What it means

NonZeroExitError is thrown by the `exitedCleanly` getter when a managed subprocess terminates with a non-zero exit code and the process was not spawned with nothrow semantics. The message includes the exit code and a truncated stderr tail (last 32KB) so the caller can see why the child failed.

Source

Thrown at packages/utils/src/ptree.ts:321

	}
	get stdin(): Bun.SpawnOptions.WritableToIO<In> {
		return this.proc.stdin;
	}

	/** Raw stdout stream. Must be consumed to prevent pipe deadlock. */
	get stdout() {
		return this.proc.stdout;
	}

	/** Optional stderr stream (only when requested in spawn options). */
	get stderr() {
		return this.#stderrStream;
	}

	get exitedCleanly(): Promise<number> {
		if (this.#nothrow) return this.#exited;
		return this.#exited.then(code => {
			if (code !== 0) throw new NonZeroExitError(code, this.#stderrTail);
			return code;
		});
	}

	/** Returns the truncated stderr tail (last 32KB). */
	peekStderr() {
		return this.#stderrTail;
	}

	nothrow(): this {
		this.#nothrow = true;
		return this;
	}

	kill(reason?: Exception, gracefulMs?: number) {
		if (reason && !this.#exitReasonPending) {
			this.#exitReasonPending = reason;
			// The normalized exit promise may already have resolved from a dead

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the stderr tail in the error message to find the child's actual failure.
  2. Fix the underlying command or its arguments/inputs.
  3. If non-zero exit is acceptable, construct the process with nothrow (or use `.exited` directly) instead of `exitedCleanly`.
  4. Wrap in try/catch and handle NonZeroExitError when failure is expected.

Example fix

// before
await proc.exitedCleanly;
// after
try {
  await proc.exitedCleanly;
} catch (err) {
  console.error(err.message); // includes code + stderr tail
}
Defensive patterns

Strategy: try-catch

Type guard

function isNonZeroExitError(err: unknown): err is { code: number; stderrTail?: string } {
  return err instanceof Error && /Process exited with code \d+/.test(err.message);
}

Try / catch

try {
  await proc.exitedCleanly;
} catch (err) {
  if (/Process exited with code/.test(err.message)) {
    logger.error('child failed', { stderr: err.message });
  } else throw err;
}

Prevention

When it happens

Trigger: Awaiting `process.exitedCleanly` on any PTree-managed subprocess that exits with code != 0 — e.g. a build step returning 1, a shell command hitting a missing binary (127), or a killed process (signal-derived codes).

Common situations: Running git/jj or package-manager commands via execText/captureText helpers where the command fails; scripts assuming success; flaky network commands.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/b0d57647a710f6f1. Report an issue: GitHub.