oxc-project/oxc · error · OxcDiagnostic

Do not throw undefined

Error message

Do not throw undefined

What it means

The `is_undef` variant of `no-throw-literal`: it fires specifically on `throw undefined;`. Throwing `undefined` is the worst case of literal throwing — the catch site receives nothing at all, `e instanceof Error` is false, and loggers print `undefined` with no message or stack. The rule distinguishes it with the dedicated message "Do not throw undefined" while sharing the same help text.

Source

Thrown at crates/oxc_linter/src/rules/eslint/no_throw_literal.rs:12

use oxc_ast::{AstKind, ast::Expression};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};

use crate::{AstNode, ast_util::could_be_error, context::LintContext, rule::Rule};

fn no_throw_literal_diagnostic(span: Span, is_undef: bool) -> OxcDiagnostic {
    let message =
        if is_undef { "Do not throw undefined" } else { "Expected an error object to be thrown" };

    OxcDiagnostic::warn(message)
        .with_help("Throwing literals or non-Error objects is not recommended. Use an Error object instead.")
        .with_label(span)
}

#[derive(Debug, Default, Clone)]
pub struct NoThrowLiteral;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Disallows throwing literals or non-Error objects as exceptions.
    ///
    /// ::: warning
    /// This rule has been deprecated, please instead use [typescript/only-throw-error](https://oxc.rs/docs/guide/usage/linter/rules/typescript/only-throw-error.html).
    /// The typescript rule is more reliable than the Javascript version, as it has less false positive, and can catch more cases.
    /// :::
    ///
    /// ### Why is this bad?

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Replace with a real error: `throw new Error('...')` describing why the code path is unreachable or invalid.
  2. If this was a stub, implement the real error path instead of throwing undefined.
  3. Audit sibling throws in the same file for the same refactor residue.

Example fix

// before
try {
  parse(input);
} catch (err) {
  throw undefined; // loses everything
}

// after
try {
  parse(input);
} catch (err) {
  throw new Error('parse failed', { cause: err });
}
Defensive patterns

Strategy: type-guard

Validate before calling

// never let a refactor leave a bare undefined throw
function required(value: unknown, msg = 'unreachable'): Error {
  return new Error(msg);
}
// use: throw required(x);

Type guard

function isThrownable(value: unknown): value is Error {
  return value instanceof Error;
}

Try / catch

catch (e) {
  if (!(e instanceof Error)) throw new Error('unnamed failure'); // undefined throw lands here
  throw e;
}

Prevention

When it happens

Trigger: `throw undefined;` where the thrown expression resolves to the `undefined` identifier or `void 0`. Common after refactors where a variable holding an error is renamed/deleted and the throw left behind.

Common situations: Refactoring `throw err` into code where `err` became undefined; placeholder throws (`throw TODO`) written during stubbing; transpiled code from older reject(undefined) promise patterns.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/63f0c3cf12dd1681. Report an issue: GitHub.