Skip to content

eslint-factory: no-throw-plain-object should offer an Object.assign(new Error(...)) suggestion #45050

Description

@github-actions

Rule

no-throw-plain-object (eslint-factory/src/rules/no-throw-plain-object.ts)

Summary

The rule correctly flags throw { ... } object literals, and its diagnostic message prescribes the exact remedy:

Use new Error(message) instead; attach extra fields with Object.assign(new Error(message), { ... }) if needed.

However the rule sets no hasSuggestions and provides no suggestion/fixer. It reports the problem but cannot help apply the fix it recommends. This matters because the flagged population is large and grounded.

Grounding (live corpus)

There are 22 production throw { ... } sites the rule flags today:

  • actions/setup/js/mcp_server_core.cjs — 10 sites (e.g. lines 721, 737, 749, 757, 765, 776, 781, 791, 801, 817)
  • actions/setup/js/safe_outputs_handlers.cjs — 12 sites (e.g. lines 292, 1776, 1819, 1888, 1959, 1966, 1973, 1996, 2005, 2066, ...)

All are JSON-RPC 2.0 error objects of the shape:

throw {
  code: -32602,
  message: "Invalid params: 'name' must be a string",
  // sometimes: data: { ... }
};

These lose .stack, are not instanceof Error, and are incompatible with the very error utilities other factory rules exist to protect (getErrorMessage, no-unsafe-catch-error-property, no-json-stringify-error). Converting them to Object.assign(new Error(message), { code, data }) preserves the code/data own-properties that the JSON-RPC transport reads, adds a stack trace, and makes the value Error-compatible — a strict improvement with no consumer-visible regression for code that reads e.code / e.message / e.data.

Proposed refinement

Add meta.hasSuggestions: true and a manual suggestion (not an auto-fix, given transport-serialization nuances) that rewrites:

throw { message: <expr>, code: -32602, data: {...} };
// →
throw Object.assign(new Error(<expr>), { code: -32602, data: {...} });

Behavior to specify with tests:

  1. Has a message property → use its value as the new Error(...) argument and drop message from the residual object.
  2. No message propertythrow Object.assign(new Error(), { ...allProps }); (or new Error() with the full object).
  3. Empty object throw {} → suggest throw new Error();.
  4. Residual becomes empty after removing message (e.g. throw { message: x }) → suggest throw new Error(x); (skip the empty Object.assign).
  5. Computed keys, spread elements, shorthand, non-Literal message, method/getter properties → either handle explicitly or skip the suggestion (still report). Do not emit a syntactically-invalid rewrite.
  6. Preserve source formatting / indentation for multi-line object literals.

Acceptance criteria

  • no-throw-plain-object sets hasSuggestions: true and emits a suggestion covering cases 1–4 above.
  • The suggestion is skipped (report-only) for the unhandled forms in case 5.
  • Unit tests cover: with-message, without-message, empty-object, message-only, data-bearing JSON-RPC object, computed-key skip, spread skip.
  • Re-verify against mcp_server_core.cjs / safe_outputs_handlers.cjs: the suggestion produces valid, behavior-preserving rewrites for the { code, message, data } shape.

Notes

Use a suggestion, not a fixable autofix: some transport code may JSON.stringify the caught value, where an Error's non-enumerable message would be dropped (no-json-stringify-error territory). Keeping it opt-in lets a human confirm the catch/serialize side per call site.

Filed by the ESLint Refiner daily review (grounded, high-impact).

Generated by 🤖 ESLint Refiner · 354.5 AIC · ⌖ 12.7 AIC · ⊞ 4.6K ·

  • expires on Jul 18, 2026, 10:27 PM UTC-08:00

Metadata

Metadata

Assignees

No one assigned

    Labels

    cookieIssue Monster Loves Cookies!eslint

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions