Skip to content

Latest commit

 

History

History
80 lines (55 loc) · 2.32 KB

File metadata and controls

80 lines (55 loc) · 2.32 KB

defensive.stringified-unknown-errors

Flags code that flattens unknown caught values into generic strings like error.message or String(error).

  • Family: defensive
  • Severity: strong
  • Scope: file
  • Requires: file.ast

How it works

The rule looks for conditional expressions like:

  • error instanceof Error ? error.message : String(error)
  • err instanceof Error ? err.message : String(err)
  • equivalent forms assigned into message / error properties or returned directly

This pattern is common in generated wrapper code that wants a quick printable error value. It is sometimes reasonable, but repeated use often flattens richer failure objects into plain strings and makes downstream diagnostics more generic.

To avoid obvious vendored noise, the rule skips very large bundled/generated files over 5000 logical lines.

Flagged examples

catch (error) {
  return { success: false, error: error instanceof Error ? error.message : String(error) };
}

setError(err instanceof Error ? err.message : String(err));

const message = error instanceof Error ? error.message : String(error);

Usually ignored

catch (error) {
  throw error;
}

catch (error) {
  logger.error({ error });
  return { success: false, error };
}

How to fix / do this better

Prefer preserving structured error information over collapsing everything into a string.

Better options:

  • propagate the original error object
  • log structured fields and keep the original error attached
  • map errors into typed domain variants instead of generic message strings
  • stringify only at the final UI or logging boundary
catch (error) {
  logger.error({ error });
  return { success: false, error };
}

If the UI really needs a display string, derive it at the edge of the system rather than erasing the richer error earlier in the flow.

Scoring

Each unknown-error stringification site adds 2 points. The file total is capped at 8.

Benchmark signal

Small pinned rule benchmark (manifest):

  • Signal rank: #4 of 9
  • Signal score: 0.70 / 1.00
  • Best separating metric: findings / file (0.80)
  • Hit rate: 4/6 AI repos vs 1/5 mature OSS repos
  • Full results: rule signal report