A name written in code has to stand for something. When the engine looks for that name everywhere it is allowed to look and finds nothing, it stops dead.
The message is short and unambiguous, which makes this one of the easiest errors to fix, with a single exception.
Definition
A ReferenceError is thrown when an identifier in use is declared in no reachable scope. The value is not wrong: the name simply does not exist.
try {
console.log(user);
} catch (err) {
console.log(err.name); // ReferenceError
console.log(err.message); // user is not defined
}
// No error at all, because typeof tolerates unknown names
console.log(typeof user); // undefinedtypeof is the one operation that tolerates an unknown name. It is a useful survival for testing whether a global object is present without breaking.
The three usual causes
- A typo. The most common case, and the one an editor flags before the code even runs.
- A forgotten import. The name exists in another file, but nothing brought it into this one.
- A global object absent from the environment.
documentandwindowdo not exist under Node.js,processdoes not exist in a browser.
That last point accounts for most of the ReferenceError messages seen in a modern project, where the same code may run on the server and then in the browser.
The temporal dead zone
This is the exception announced above, and it throws people because the name does exist. A variable declared with let or const is reserved from the moment its block is entered, but stays unreachable until its declaration line.
try {
console.log(counter);
let counter = 1;
} catch (err) {
console.log(err.message); // Cannot access 'counter' before initialization
}
// With var, no error but an unusable value
console.log(total); // undefined
var total = 10;The message differs sharply from is not defined: it tells you the declaration sits further down in the same block. With var, the same code raises nothing and hands back undefined, producing a silent defect instead of a clear error.
Outside strict mode, assigning to an undeclared name creates a global variable rather than raising an error. Modules are always in strict mode, which restores the expected ReferenceError: one more reason to work in modules.
Frequently asked questions
Why does document is not defined show up when the server starts?
Because the code runs on the server first, where the browser document does not exist. The read then has to move into an effect fired after display, or be guarded by a test for the presence of the global object.
How does it differ from a TypeError?
A ReferenceError happens before any use: the name cannot be found. A TypeError comes after: the name exists, so does the value, but that value does not support the operation asked of it. The first is fixed in the declaration, the second in the data.
How do you avoid these errors instead of meeting them at runtime?
A static analyzer reports them as you type: a dedicated ESLint rule refuses any undeclared name, and TypeScript flatly refuses to compile. Both turn a runtime failure into a warning in the editor, which changes the cost of the mistake entirely.