Rule
no-err-stack-then-string-fallback (eslint-factory/src/rules/no-err-stack-then-string-fallback.ts).
Problem
The rule's only suggestion (replaceWithGetErrorMessage, lines 94-102) unconditionally rewrites the matched <errVar> instanceof Error ? <errVar>.stack : String(<errVar>) (or &&-guarded) ternary to getErrorMessage(<errVar>), with no check that getErrorMessage is actually imported/resolvable in the file. The message text itself even says "ensure getErrorMessage is imported from error_helpers.cjs before applying" (line 61) — i.e. the rule authors knew this was a precondition, but never encoded it as a check.
Three sibling rules that offer the identical getErrorMessage(...) suggestion already guard against exactly this failure mode with a hasResolvableLocalBinding(node, "getErrorMessage") check: no-json-stringify-error.ts, prefer-get-error-message-over-string.ts, and no-caught-error-interpolation.ts. no-err-stack-then-string-fallback.ts is missing this same gate.
Grounded evidence
actions/setup/js/copilot_sdk_driver.cjs:146-150:
if (require.main === module) {
main().catch(err => {
process.stderr.write(`[copilot-sdk-driver] unhandled error: ${err instanceof Error ? err.stack : String(err)}\n`);
process.exit(1);
});
}
This exactly matches the rule's <errVar> instanceof Error ? <errVar>.stack : String(<errVar>) pattern and correctly fires. But copilot_sdk_driver.cjs has zero references to getErrorMessage anywhere in the file (confirmed by grep) and never imports error_helpers.cjs. Accepting the suggested fix here replaces this line with getErrorMessage(err), which throws ReferenceError: getErrorMessage is not defined — inside the top-level unhandled-rejection handler itself, i.e. the fix silently breaks the last-resort error-reporting path for the entire script.
Ask
Add the same hasResolvableLocalBinding(node, "getErrorMessage") gate already used by no-json-stringify-error.ts / prefer-get-error-message-over-string.ts / no-caught-error-interpolation.ts to no-err-stack-then-string-fallback.ts, so the suggest array is empty (diagnostic still reported, just no unsafe autofix) when getErrorMessage isn't resolvable in scope.
Acceptance criteria
- New test case: file without a
getErrorMessage import/binding — rule still reports the diagnostic but suggest is empty (no unresolvable-reference autofix offered).
- Existing test case(s) where
getErrorMessage is resolvable continue to offer the suggestion unchanged.
- Re-verify
copilot_sdk_driver.cjs:148 after the fix: diagnostic still fires, no suggestion offered (until error_helpers.cjs is actually imported there).
Generated by 🤖 ESLint Refiner · agent · 669.3 AIC · ⌖ 7.12 AIC · ⊞ 4.9K · ◷
Rule
no-err-stack-then-string-fallback(eslint-factory/src/rules/no-err-stack-then-string-fallback.ts).Problem
The rule's only suggestion (
replaceWithGetErrorMessage, lines 94-102) unconditionally rewrites the matched<errVar> instanceof Error ? <errVar>.stack : String(<errVar>)(or&&-guarded) ternary togetErrorMessage(<errVar>), with no check thatgetErrorMessageis actually imported/resolvable in the file. The message text itself even says "ensure getErrorMessage is imported from error_helpers.cjs before applying" (line 61) — i.e. the rule authors knew this was a precondition, but never encoded it as a check.Three sibling rules that offer the identical
getErrorMessage(...)suggestion already guard against exactly this failure mode with ahasResolvableLocalBinding(node, "getErrorMessage")check:no-json-stringify-error.ts,prefer-get-error-message-over-string.ts, andno-caught-error-interpolation.ts.no-err-stack-then-string-fallback.tsis missing this same gate.Grounded evidence
actions/setup/js/copilot_sdk_driver.cjs:146-150:This exactly matches the rule's
<errVar> instanceof Error ? <errVar>.stack : String(<errVar>)pattern and correctly fires. Butcopilot_sdk_driver.cjshas zero references togetErrorMessageanywhere in the file (confirmed by grep) and never importserror_helpers.cjs. Accepting the suggested fix here replaces this line withgetErrorMessage(err), which throwsReferenceError: getErrorMessage is not defined— inside the top-level unhandled-rejection handler itself, i.e. the fix silently breaks the last-resort error-reporting path for the entire script.Ask
Add the same
hasResolvableLocalBinding(node, "getErrorMessage")gate already used byno-json-stringify-error.ts/prefer-get-error-message-over-string.ts/no-caught-error-interpolation.tstono-err-stack-then-string-fallback.ts, so thesuggestarray is empty (diagnostic still reported, just no unsafe autofix) whengetErrorMessageisn't resolvable in scope.Acceptance criteria
getErrorMessageimport/binding — rule still reports the diagnostic butsuggestis empty (no unresolvable-reference autofix offered).getErrorMessageis resolvable continue to offer the suggestion unchanged.copilot_sdk_driver.cjs:148after the fix: diagnostic still fires, no suggestion offered (untilerror_helpers.cjsis actually imported there).