Skip to content

eslint-factory: require-error-cause-in-rethrow's expressionReferencesCatchVar skips LogicalExpression/ConditionalExpression (idi [Content truncated due to length] #43948

Description

@github-actions

Summary

In require-error-cause-in-rethrow, expressionReferencesCatchVar (src/rules/require-error-cause-in-rethrow.ts:27-56) decides whether the rethrown new Error(...) message references the caught variable. It recurses through Identifier, CallExpression, TemplateLiteral, BinaryExpression, and MemberExpression — but it does not handle several node types that routinely wrap error values in defensive message-building:

  • LogicalExpression (err.message || "unknown", err ?? fallback, err.message && ...) — note ESTree models ||/&&/?? as LogicalExpression, not BinaryExpression, so the existing BinaryExpression branch does not cover them.
  • ConditionalExpression (cond ? err.message : "n/a").
  • SequenceExpression and array/object literals holding the catch var (new Error([err].join('')), less common).

Any rethrow whose reference to the caught variable lives only inside one of these unhandled nodes returns false → the rule does not fire → the cause chain is silently dropped.

Grounding

actions/setup/js/create_pull_request.cjs:589 builds its message with a LogicalExpression fallback:

throw new Error(`Failed to delete existing remote branch "${branchName}" for reuse with recreate-ref: ${message || String(err)}`);

The ${message || String(err)} template expression is a LogicalExpression; expressionReferencesCatchVar bails out at the unhandled node type and never reaches the String(err) reference. (This site is also affected by the alias blindspot filed separately — both gaps independently hide it; fixing only one is insufficient.)

The x.message || "fallback" and cond ? x.message : default idioms are standard defensive error-formatting in JS, so this is a broad precision gap even where no aliasing is involved.

Acceptance criteria

  1. Extend expressionReferencesCatchVar to traverse LogicalExpression (both operands), ConditionalExpression (test/consequent/alternate), and SequenceExpression (all expressions). Consider array/object literals for completeness if cheap.
  2. Add RuleTester coverage:
    • invalid: catch (err) { throw new Error(\x: ${err.message || 'unknown'}`); }`
    • invalid: catch (err) { throw new Error(cond ? err.message : 'n/a'); }
    • valid: same shapes but with { cause: err } present.
    • valid (regression guard): a logical expression that does NOT reference the catch var, e.g. throw new Error(a || b);, must remain un-flagged.
  3. Confirm no new false positives on the existing valid fixtures (the "Something went wrong" / unrelated-message cases).

Notes

Keep the traversal purely syntactic (no type info / no cross-statement dataflow), consistent with the rest of the rule. Recursion is already bounded by AST depth.

Generated by 🤖 ESLint Refiner · 197.1 AIC · ⌖ 13.3 AIC · ⊞ 4.6K ·

  • expires on Jul 13, 2026, 10:39 PM UTC-08:00

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions