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
- Replace with a real error: `throw new Error('...')` describing why the code path is unreachable or invalid.
- If this was a stub, implement the real error path instead of throwing undefined.
- 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
- Treat `throw undefined` as a stub marker and never merge it.
- When renaming/deleting error variables, re-run the linter on the file immediately.
- Keep no-throw-literal enabled so the stub is caught in CI.
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
- Expected an error object to be thrown
- Unexpected use of `undefined`
- Unsafe usage of optional chaining
- '{ident_name}' is always 'undefined' because it's never assi
- Empty array binding pattern
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/63f0c3cf12dd1681.
Report an issue: GitHub.