expo/expo · error · Error
process.exit(${code}) called in Hermes runtime
Error message
process.exit(${code}) called in Hermes runtime What it means
Thrown by the process polyfill's exit() method in globals.js. Since the device-transformer runs inside the Expo Go app's Hermes/JSC runtime (not a Node process), calling process.exit would crash the app. The polyfill deliberately throws to make any accidental exit call loud and diagnosable rather than silently killing the runtime.
Source
Thrown at apps/expo-go/tools/device-transformer/globals.js:294
const proc = {
env: {},
argv: ['jsc', 'device-transformer'],
execArgv: [],
platform: 'darwin',
arch: 'arm64',
version: 'v22.14.0',
versions: { node: '22.14.0', v8: '12.4' },
pid: 1,
title: 'jsc',
browser: false,
exitCode: 0,
cwd: () => '/project',
chdir: () => {
throw new Error('process.chdir not supported');
},
nextTick: (fn, ...args) => g.queueMicrotask(() => fn(...args)),
exit: (code) => {
throw new Error('process.exit(' + code + ') called in Hermes runtime');
},
hrtime: Object.assign(
(prev) => {
const ms = Date.now();
const s = Math.floor(ms / 1000),
ns = (ms % 1000) * 1e6;
if (prev) {
let ds = s - prev[0],
dns = ns - prev[1];
if (dns < 0) {
ds--;
dns += 1e9;
}
return [ds, dns];
}
return [s, ns];
},
{ bigint: () => BigInt(Date.now()) * 1000000n }View on GitHub (pinned to b09195aac2)
Solutions
- Trace the stack to find which module calls process.exit and what condition triggers it — the error path likely indicates a real problem (missing config, version mismatch).
- Fix the underlying condition that causes the exit call.
- If the exit is on a dead code path, stub the module via an esbuild onLoad handler.
Example fix
// before — throws, potentially masking the real issue
exit: (code) => {
throw new Error('process.exit(' + code + ') called in Hermes runtime');
},
// after — log the stack before throwing for easier diagnosis
exit: (code) => {
console.error('process.exit(' + code + ') called from:', new Error().stack);
throw new Error('process.exit(' + code + ') called in Hermes runtime');
}, Defensive patterns
Strategy: try-catch
Validate before calling
// Scan bundled source for process.exit usage
function scanForProcessExit(sourceFiles) {
const offenders = [];
for (const { path, content } of sourceFiles) {
if (/process\.exit\s*\(/.test(content)) {
offenders.push(path);
}
}
return offenders;
} Try / catch
// Wrap transform calls to catch process.exit throws
try {
transformModule(source, filename, moduleId, depIds);
} catch (err) {
if (err.message.includes('process.exit(')) {
// The real error is whatever caused the exit call — log full stack
console.error('Underlying error caused process.exit:', err.stack);
}
throw err;
} Prevention
- Investigate any process.exit call in bundled code — it usually masks a real error condition.
- Patch modules that call process.exit on dead code paths via esbuild onLoad handlers.
- Fix the underlying condition (missing config, version mismatch) that triggers the exit.
When it happens
Trigger: Any bundled module calls process.exit(code) during the on-device transform. The polyfill constructs an error with the exit code in the message.
Common situations: A babel plugin, metro utility, or dependency calls process.exit on an error path (e.g., version mismatch, missing config); a dependency's CLI bootstrap code calls exit during module initialization.
Related errors
- process.chdir not supported
- process.binding not supported
- setInterval is not available in the Hermes runtime
- Buffer.from: unsupported encoding ${enc}
- Buffer.from: unsupported value
AI-assisted analysis of expo/expo@b09195aac2 (2026-08-12).
Data as JSON: /api/errors/5d1d6f13fcb6f566.
Report an issue: GitHub.