Summary
The no-core-error-then-process-exit suggestion fix replaces core.error(msg); process.exit(nonzero) with core.setFailed(msg); return; (when inside a function). But return and process.exit(nonzero) are not control-flow-equivalent: process.exit aborts the whole process, whereas return only exits the enclosing function and lets callers keep running. When the flagged pair sits inside a helper that returns a value, the autofix silently converts a hard abort into a fall-through that returns undefined.
Grounded evidence
actions/setup/js/convert_gateway_config_shared.cjs (a standalone node step, so it is linted):
function requireEnvVar(name) {
const value = process.env[name];
if (!value) {
core.error(`ERROR: ${name} environment variable is required`);
process.exit(1); // flagged pair
}
return value; // contract: returns a string
}
Applying the suggested fix yields core.setFailed(...); return; — the function now returns undefined, and the caller loadGatewayContext (const gatewayOutput = requireEnvVar("MCP_GATEWAY_OUTPUT"), lines 66-72) continues with gatewayOutput === undefined, then calls fs.existsSync(undefined), etc. The same shape recurs at :66-72 (loadGatewayContext) and in convert_gateway_config_copilot.cjs:71-72.
These are node script.cjs steps where process.exit(1) is the correct step-failure signal; there are no Actions post-hooks to bypass.
Why it matters
- The autofix is unsafe: it turns a clean fatal exit into a continue-with-
undefined bug.
- The diagnostic asserts
process.exit(nonzero) "bypasses the proper GitHub Actions failure lifecycle" / skips "cleanup hooks", which does not hold for standalone node scripts (the dominant case here), where a non-zero exit is exactly how the step fails.
Acceptance criteria
- Suppress the suggestion (report-only,
suggest: []) when the enclosing function returns a value or is not the module entrypoint / top-level main, since return cannot reproduce process.exit's process-abort semantics.
- Add tests: (a) pair inside a value-returning helper -> no autofix; (b) pair at module top level -> existing autofix; (c) pair inside async
main() entrypoint -> autofix retained.
- Re-verify
convert_gateway_config_shared.cjs:45-52 and :66-72, and convert_gateway_config_copilot.cjs:71-72: these must not receive a mechanical return autofix, or the fix must preserve abort semantics (e.g. throw).
- Optionally soften the message to acknowledge that a non-zero
process.exit does fail the step in standalone-node contexts, so the rule's value is the annotation/lifecycle nuance rather than "exit is always wrong".
Generated by 🤖 ESLint Refiner · 312.3 AIC · ⌖ 12.9 AIC · ⊞ 4.6K · ◷
Summary
The
no-core-error-then-process-exitsuggestion fix replacescore.error(msg); process.exit(nonzero)withcore.setFailed(msg); return;(when inside a function). Butreturnandprocess.exit(nonzero)are not control-flow-equivalent:process.exitaborts the whole process, whereasreturnonly exits the enclosing function and lets callers keep running. When the flagged pair sits inside a helper that returns a value, the autofix silently converts a hard abort into a fall-through that returnsundefined.Grounded evidence
actions/setup/js/convert_gateway_config_shared.cjs(a standalonenodestep, so it is linted):Applying the suggested fix yields
core.setFailed(...); return;— the function now returnsundefined, and the callerloadGatewayContext(const gatewayOutput = requireEnvVar("MCP_GATEWAY_OUTPUT"), lines 66-72) continues withgatewayOutput === undefined, then callsfs.existsSync(undefined), etc. The same shape recurs at:66-72(loadGatewayContext) and inconvert_gateway_config_copilot.cjs:71-72.These are
node script.cjssteps whereprocess.exit(1)is the correct step-failure signal; there are no Actions post-hooks to bypass.Why it matters
undefinedbug.process.exit(nonzero)"bypasses the proper GitHub Actions failure lifecycle" / skips "cleanup hooks", which does not hold for standalonenodescripts (the dominant case here), where a non-zero exit is exactly how the step fails.Acceptance criteria
suggest: []) when the enclosing function returns a value or is not the module entrypoint / top-levelmain, sincereturncannot reproduceprocess.exit's process-abort semantics.main()entrypoint -> autofix retained.convert_gateway_config_shared.cjs:45-52and:66-72, andconvert_gateway_config_copilot.cjs:71-72: these must not receive a mechanicalreturnautofix, or the fix must preserve abort semantics (e.g.throw).process.exitdoes fail the step in standalone-node contexts, so the rule's value is the annotation/lifecycle nuance rather than "exit is always wrong".