Skip to main content
interlace
Plugin: reliabilityRules

error-message

Enforce providing a message when creating built-in Error objects for better debugging. This rule is part of eslint-plugin-reliability and provides LLM-optim

Enforce providing a message when creating built-in Error objects for better debugging. This rule is part of eslint-plugin-reliability and provides LLM-optimized error messages with suggestions.

Quick Summary

AspectDetails
SeverityError (correctness)
Auto-Fix๐Ÿ’ก Suggests fixes
CategoryError Handling
ESLint MCPโœ… Optimized for ESLint MCP integration
Best ForAll projects, especially debugging-heavy development

Rule Details

Why This Matters

IssueImpactSolution
๐Ÿ› DebuggingNo context in stack tracesAdd descriptive messages
๐Ÿ“Š Error TrackingVague errors in monitoringClear error descriptions
๐Ÿ” Root Cause AnalysisHard to identify issuesActionable messages
๐Ÿ“ DocumentationCode intent unclearSelf-documenting errors

Configuration

OptionTypeDefaultDescription
allowEmptyCatchbooleanfalseAllow empty errors in catch blocks

Covered Error Constructors

ConstructorExample
Errornew Error('message')
TypeErrornew TypeError('message')
ReferenceErrornew ReferenceError('message')
SyntaxErrornew SyntaxError('message')
RangeErrornew RangeError('message')
EvalErrornew EvalError('message')
URIErrornew URIError('message')

What counts as a missing message

The constructor must be the built-in one โ€” decided by binding, so a local class named Error is not it, and globalThis.Error is. The message is missing when the code proves it empty: no argument, undefined, a literal that is not a string (null, 42), or a string literal or template with nothing but whitespace in it. A variable, a call or a template with a substitution may carry a message and is not reported.

Examples

โŒ Incorrect

// Missing message
throw new Error();
throw new TypeError();
throw new RangeError();

// Empty string message
throw new Error('');
throw new Error('   ');
throw new Error(``);
throw new Error(undefined);

// Function call without new (also caught)
throw Error();

โœ… Correct

// Descriptive messages
throw new Error('User not found in database');
throw new TypeError('Expected string, received number');
throw new RangeError('Value must be between 0 and 100');

// Dynamic messages
throw new Error(`Failed to process item ${id}`);
throw new Error(errorMessage);

// Template literals
throw new Error(`Invalid configuration: ${JSON.stringify(config)}`);

Configuration Examples

Basic Usage

{
  rules: {
    'reliability/error-message': 'error'
  }
}

Allow in Catch Blocks

{
  rules: {
    'reliability/error-message': ['error', {
      allowEmptyCatch: true
    }]
  }
}

Best Practices

Good Error Messages

// โœ… Specific and actionable
throw new Error('Database connection failed: connection string missing');
throw new TypeError('Expected array of users, received undefined');
throw new RangeError('Page number must be positive integer, got -5');

// โœ… Include relevant context
throw new Error(`Failed to fetch user ${userId}: ${response.status}`);
throw new Error(`Invalid email format: ${email}`);

// โœ… Suggest solution
throw new Error('API key not found. Set OPENAI_API_KEY environment variable.');

Bad Error Messages

// โŒ Too vague
throw new Error('Something went wrong');
throw new Error('Error');
throw new Error('Invalid input');

// โŒ No message at all
throw new Error();

// โŒ Generic
throw new TypeError('Type error');

Error Context Pattern

// โœ… Good: Include operation context
function fetchUser(id: string) {
  const user = db.find(id);
  if (!user) {
    throw new Error(`User not found with id: ${id}`);
  }
  return user;
}

// โœ… Good: Include validation context
function validateAge(age: number) {
  if (age < 0 || age > 150) {
    throw new RangeError(`Age must be between 0 and 150, received: ${age}`);
  }
}

// โœ… Good: Include expected vs actual
function processConfig(config: unknown) {
  if (typeof config !== 'object') {
    throw new TypeError(
      `Expected config to be an object, received ${typeof config}`
    );
  }
}

When Not To Use

ScenarioRecommendation
๐Ÿ”„ Re-throwing errorsUse allowEmptyCatch: true
๐Ÿงช Test assertionsTest frameworks handle this
๐Ÿ“ฆ Error wrappingEnsure wrapper adds message

Comparison with Alternatives

Featureerror-messageunicorn ruleManual review
All Error typesโœ… Yesโœ… Yesโš ๏ธ Manual
Function callsโœ… Error()โœ… Yesโš ๏ธ Manual
Empty string checkโœ… Yesโœ… YesโŒ No
LLM-Optimizedโœ… YesโŒ NoโŒ No
ESLint MCPโœ… OptimizedโŒ NoโŒ No

Further Reading

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Dynamic Variable References

Why: Static analysis cannot trace values stored in variables or passed through function parameters.

// โŒ NOT DETECTED - Value from variable
const value = externalSource();
processValue(value); // Variable origin not tracked

Mitigation: Implement runtime validation and review code manually. Consider using TypeScript branded types for validated inputs.

Imported Values

Why: When values come from imports, the rule cannot analyze their origin or construction.

// โŒ NOT DETECTED - Value from import
import { getValue } from './helpers';
processValue(getValue()); // Cross-file not tracked

Mitigation: Ensure imported values follow the same constraints. Use TypeScript for type safety.

Use this rule

npm install -D oxlint-plugin-reliability
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-reliability"],  "rules": {    "reliability/error-message": "error"  }}
vite.config.ts
import { defineConfig } from 'vite-plus';export default defineConfig({  lint: {    jsPlugins: ['oxlint-plugin-reliability'],    rules: {      'reliability/error-message': 'error',    },  },});

oxlint-plugin-reliability re-exports eslint-plugin-reliability/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.

Did this rule catch something? Star the repo to get new CWE coverage as we ship it โ€” or follow the AI-code-security benchmarks behind these rules.