Rule
require-spawnsync-error-check
Summary
The rule only recognizes a .error check when .error is accessed directly on the result binding in a guarding position (e.g. if (result.error) throw result.error). If the value is first read into a local single-assignment alias and the alias is then guarded, the rule fails to connect the two and reports a false positive, even though the code correctly inspects the spawn error.
This affects both analysis branches (simple identifier and object destructuring), because isGuardingErrorUsage is only ever evaluated against the .error member expression / destructured binding itself — it does not follow a const alias = result.error (or const alias = error) initializer to the later guard of alias.
Reproduction (currently flagged, should be valid)
// simple-identifier branch
const result = spawnSync("git", ["status"]);
const spawnErr = result.error; // read consumed by a VariableDeclarator init — not a guarding position
if (spawnErr) throw spawnErr; // guards `spawnErr`, which the rule never links back to result.error
if (result.status !== 0) throw new Error("failed");
// object-pattern branch — same indirection
const { status, error } = spawnSync("zip", ["-v"]);
const e = error;
if (e) throw e;
In each case the spawn-level error IS checked, but the rule emits missingErrorCheck.
Root cause
In the simple-identifier branch the guard scan requires parent.type === MemberExpression && parent.property.name === "error" && isGuardingErrorUsage(parent). When result.error is the init of a VariableDeclarator, isGuardingErrorUsage walks to the VariableDeclarator parent, which it does not handle, and returns false. The object-pattern branch has the analogous gap: isGuardingErrorUsage(ref.identifier) returns false when the error binding is only read into another variable.
Grounding / severity
Logic-derived. No live escaping site exists in actions/setup/js today — the only real spawnSync caller (apply_samples.cjs:97, runGit) checks neither .status-plus-.error, so it is correctly flagged as a true positive. This report is a precision / false-positive-prevention refinement to keep the rule from penalizing a common const err = result.error; if (err) ... extraction style once such code appears.
Acceptance criteria
- Recognize a single-assignment alias of the spawn error as a valid guard: when
result.error (simple-identifier branch) or the destructured error binding (object-pattern branch) is read into a const/let binding, treat a later guard of that binding as satisfying the error check.
- Add valid-case tests:
const result = spawnSync("git", ["status"]); const e = result.error; if (e) throw e;
const { status, error } = spawnSync("zip", ["-v"]); const e = error; if (e) throw e;
- Keep an invalid-case test where the alias is read but never guarded (e.g.
const e = result.error; core.info(String(e));) to confirm the fix does not over-broaden.
- Document in the rule description any indirection that remains out of scope (e.g. passing
result to a helper that checks .error).
Generated by 🤖 ESLint Refiner · 429.1 AIC · ⌖ 13.4 AIC · ⊞ 4.6K · ◷
Rule
require-spawnsync-error-checkSummary
The rule only recognizes a
.errorcheck when.erroris accessed directly on the result binding in a guarding position (e.g.if (result.error) throw result.error). If the value is first read into a local single-assignment alias and the alias is then guarded, the rule fails to connect the two and reports a false positive, even though the code correctly inspects the spawn error.This affects both analysis branches (simple identifier and object destructuring), because
isGuardingErrorUsageis only ever evaluated against the.errormember expression / destructured binding itself — it does not follow aconst alias = result.error(orconst alias = error) initializer to the later guard ofalias.Reproduction (currently flagged, should be valid)
In each case the spawn-level error IS checked, but the rule emits
missingErrorCheck.Root cause
In the simple-identifier branch the guard scan requires
parent.type === MemberExpression && parent.property.name === "error" && isGuardingErrorUsage(parent). Whenresult.erroris theinitof aVariableDeclarator,isGuardingErrorUsagewalks to theVariableDeclaratorparent, which it does not handle, and returnsfalse. The object-pattern branch has the analogous gap:isGuardingErrorUsage(ref.identifier)returns false when theerrorbinding is only read into another variable.Grounding / severity
Logic-derived. No live escaping site exists in
actions/setup/jstoday — the only realspawnSynccaller (apply_samples.cjs:97,runGit) checks neither.status-plus-.error, so it is correctly flagged as a true positive. This report is a precision / false-positive-prevention refinement to keep the rule from penalizing a commonconst err = result.error; if (err) ...extraction style once such code appears.Acceptance criteria
result.error(simple-identifier branch) or the destructurederrorbinding (object-pattern branch) is read into aconst/letbinding, treat a later guard of that binding as satisfying the error check.const result = spawnSync("git", ["status"]); const e = result.error; if (e) throw e;const { status, error } = spawnSync("zip", ["-v"]); const e = error; if (e) throw e;const e = result.error; core.info(String(e));) to confirm the fix does not over-broaden.resultto a helper that checks.error).