Skip to content

[eslint-miner] eslint-factory: add no-string-fallback-for-non-string-message rule - #55052

Draft
github-actions[bot] wants to merge 1 commit into
mainfrom
eslint-miner/no-string-fallback-for-non-string-message-449359239243a6f0
Draft

[eslint-miner] eslint-factory: add no-string-fallback-for-non-string-message rule#55052
github-actions[bot] wants to merge 1 commit into
mainfrom
eslint-miner/no-string-fallback-for-non-string-message-449359239243a6f0

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Summary

New ESLint rule no-string-fallback-for-non-string-message for eslint-factory (targets actions/setup/js/**/*.cjs).

Motivation

Issue #55014 (MCE-006, Safe Outputs Conformance) found a real bug in error_helpers.cjs's getErrorMessage():

} else if (error && typeof error === "object" && "message" in error && typeof error.message === "string") {
  message = error.message;
} else {
  message = String(error);   // <-- stringifies the WHOLE error object
}

When a thrown value has a non-string .message property, this pattern falls back to stringifying the entire container object instead of coercing the message itself — producing "[object Object]" in user-facing error output (a violation of the safe-outputs spec, Section 8.2).

Scanning actions/setup/js for the same shape (typeof X.message === "string" ? X.message : String(<different-expr>)) turned up 4 more live occurrences of the identical anti-pattern:

  • dispatch_workflow.cjs:304
  • route_slash_command.cjs:374
  • log_parser_shared.cjs:1009
  • safeoutputs_cli.cjs:48

This is a recurring, structural bug shape — not a one-off — so a rule is warranted to prevent regressions and catch new instances.

Rule design

no-string-fallback-for-non-string-message flags a ConditionalExpression where:

  1. The test is typeof <chain>.message === "string" (optionally guarded by a leading &&, e.g. typeof x === "object" && typeof x.message === "string").
  2. The consequent re-reads the same <chain>.message.
  3. The alternate is String(<container>), where <container> is a different expression than <chain>.message.

Low false-positive risk: it only fires when the fallback's String() argument is structurally distinct from the tested .message chain — legitimate cases where the fallback already coerces the same chain (String(err.message)) are accepted as valid.

Registered at warn severity (consistent with most rules in this factory) since this needs a case-by-case fix decision at each call site (the correct fallback value differs contextually).

Validation

cd eslint-factory && npm install
cd eslint-factory && npm run build      # tsc passes
cd eslint-factory && npm run lint:setup-js

lint:setup-js output includes the 4 real flagged sites (evidence of value, 0 false positives observed across full actions/setup/js scan):

actions/setup/js/dispatch_workflow.cjs
  304:115  warning  ... gh-aw-custom/no-string-fallback-for-non-string-message
actions/setup/js/log_parser_shared.cjs
  1009:116 warning  ... gh-aw-custom/no-string-fallback-for-non-string-message
actions/setup/js/route_slash_command.cjs
  374:102  warning  ... gh-aw-custom/no-string-fallback-for-non-string-message
actions/setup/js/safeoutputs_cli.cjs
  48:69    warning  ... gh-aw-custom/no-string-fallback-for-non-string-message

New rule unit tests (no-string-fallback-for-non-string-message.test.ts) pass, covering valid (correct-fallback / unrelated-conditional) and invalid (container-stringified) cases.

Scope

  • Fixing the flagged call sites is left to follow-up work (the safe fallback value differs per call site — some prefer restoring an outer variable, others a nested field — and shouldn't be guessed here to avoid behavior regressions).
  • No changes outside eslint-factory/.

Generated by ESLint Miner · auto · 106.9 AIC · ⌖ 9.66 AIC · ⊞ 6.8K ·

  • expires on Aug 30, 2026, 1:03 AM UTC-08:00

Detects the pattern:
  typeof <chain>.message === "string" ? <chain>.message : String(<container>)
where the String() fallback stringifies a different (container) expression
than the .message chain under test. When .message exists but is non-string,
this silently produces "[object Object]" instead of coercing the message
value itself.

This mirrors the real bug reported in issue #55014 (error_helpers.cjs
getErrorMessage()) and the rule additionally flags 4 live occurrences of
the same anti-pattern in actions/setup/js: dispatch_workflow.cjs,
log_parser_shared.cjs, route_slash_command.cjs, and safeoutputs_cli.cjs.

Registered at warn severity in eslint.config.cjs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added automation cookie Issue Monster Loves Cookies! eslint labels Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automation cookie Issue Monster Loves Cookies! eslint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants