JAVASCRIPT / ERRORS, STRICT MODE, AND DEBUGGING
Syntax errors against runtime exceptions
Tell parse-time syntax errors from runtime exceptions apart, predict which ones try/catch can handle, and explain why a bad file never runs.
What you will learn
- Tell from the console whether a file failed to parse or failed while running
- Explain why try/catch cannot rescue a syntax error in its own file
- Recognise early errors like duplicate let or stray break as parse-time failures
- Catch runtime SyntaxErrors from JSON.parse, new RegExp, eval and new Function
Understanding Syntax errors against runtime exceptions
Before a single statement of a JavaScript file runs, the engine reads the whole file and turns it into an internal representation. If the text does not fit the grammar, that step fails, the engine reports a SyntaxError with a line and column, and it discards the entire unit: no function is defined, no variable is created, and the first line never executes. A runtime exception is the opposite situation, where the text was accepted, execution started, and then some statement asked for something impossible, such as reading a property of null or calling a number.
The practical consequence is about who can respond. A try/catch is itself code, so it only exists once the file has been parsed; wrapping a misspelled keyword in try/catch changes nothing, because the catch clause sits inside the unit the parser threw away. Runtime exceptions instead unwind through the call stack looking for a handler, which is why they can be caught, logged, retried, or replaced with a fallback value. Syntax errors are also fully deterministic — same file, same failure, whatever the input — while runtime exceptions often depend on the data you happen to receive.
The two names are less informative than they look. The parser rejects some code that contains no typo at all, such as `break` with no enclosing loop, two `let` declarations of one name, or duplicate parameter names in strict code; the specification calls these early errors and requires them to be found before execution. Meanwhile `SyntaxError` is an ordinary error class that anything can throw, and `JSON.parse`, `new RegExp`, `eval` and `new Function` all inspect text while your program runs, so their failures are catchable exceptions. Conversely, mistakes a reader would call obvious, like reassigning a `const` or reading a `let` before its declaration, are defined as runtime errors, so the file loads fine and only that line fails.
function report(label, work) {
try {
work();
console.log(label + " -> accepted");
} catch (err) {
console.log(label + " -> " + err.name);
}
}
// new Function compiles its argument while this program is running,
// so a grammar failure arrives as an ordinary throwable value.
report("compile 'return 1 +'", () => new Function("return 1 +"));
report("compile 'let a; let a;'", () => new Function("let a; let a;"));
// Both of these parsed cleanly when the file loaded; they fail on execution.
report("read length of null", () => null.length);
report("reassign a const", () => {
const n = 1;
n = 2;
});
console.log("all four were caught, so the program keeps going");A syntax error is the engine refusing the file, so nothing in it runs; a runtime exception happens inside code that is already running, so it can be caught.
Worked examples
A parse failure hides everything above it
eval compiles its whole argument before executing any of it, so a valid first line still never runs.
const source = `
console.log("this line is fine");
functin add(a, b) { return a + b; }
`;
try {
eval(source);
} catch (err) {
console.log("caught:", err.name);
console.log("the fine line above never printed");
}Example explained
Line 1eval parses the complete string first, so the console.log call is never reached even though it is correct.
Line 2`functin add(a, b)` is two identifiers in a row, a shape the grammar has no rule for, and one bad token invalidates the whole string.
Line 3The catch only works because `eval(source)` is a call in an already-parsed file; if that source sat directly in the file, this catch would have been discarded with it.
Rejections that are not typos
Some statically detectable mistakes are parse-time errors even though every token is spelled correctly.
const sources = [
"break;",
"let x; var x;",
"'use strict'; function f(a, a) {}",
"function f(a, a) {}"
];
for (const src of sources) {
try {
new Function(src);
console.log("accepted: " + src);
} catch (err) {
console.log(err.name + ": " + src);
}
}Example explained
Line 1`break;` has no enclosing loop or switch, and that check is defined as a parse-time error rather than a runtime one.
Line 2`let x; var x;` declares one name twice in a single scope, which must be rejected before the body can run.
Line 3Duplicate parameter names are a parse-time error only in strict code, which is why the identical sloppy-mode function on the last line is accepted.
SyntaxError thrown while the program runs
JSON.parse and RegExp validate text during execution, so their SyntaxError is a normal catchable exception.
function loadFlag(text) {
try {
return JSON.parse(text).enabled;
} catch (err) {
console.log(err.name, "from JSON.parse, instanceof SyntaxError:", err instanceof SyntaxError);
return false;
}
}
console.log(loadFlag('{"enabled": true}'));
console.log(loadFlag("{enabled: true}"));
try {
new RegExp("(unclosed");
} catch (err) {
console.log("regex:", err.name);
}Example explained
Line 1The first call parses valid JSON and reads `.enabled`, so it returns true with no error at all.
Line 2`{enabled: true}` is valid JavaScript object syntax but invalid JSON, and JSON.parse reports that as a SyntaxError mid-execution.
Line 3Because the throw happens during execution, the catch runs, the function returns a fallback, and later lines still execute.
Line 4`new RegExp("(unclosed")` compiles a pattern at runtime, so a broken pattern is a runtime SyntaxError rather than a load failure.
Important notes
The parse unit is one script or module, not the page: a syntax error in one script block stops only that block while later blocks still run, so a broken file often looks like silence plus a single console error instead of a halted page.
Parse error wording is engine-specific, so rely on the reported position and on err.name rather than the sentence; also note that a strict Content-Security-Policy can refuse eval and new Function, so run these demos in a plain console.
Common mistakes
Wrapping a whole file in try/catch to silence 'Uncaught SyntaxError': the catch clause belongs to the unit the parser discarded, so nothing runs and the console message is unchanged.
Assuming every SyntaxError means a typo in your own source, so a JSON.parse call on a server response is left unguarded and one malformed body kills the whole handler.
Reading 'Assignment to constant variable' or 'Cannot access x before initialization' as a punctuation problem and hunting for a missing brace, when the file parsed cleanly and the fix is to change the declaration.
Try it yourself
Change, predict, then run
In a browser console, run try { eval('console.log("first"); functin f() {}') } catch (e) { console.log(e.name) } and confirm that "first" never prints. Then paste functin f() {} on its own line and note that no try/catch could have caught it.
Open the JavaScript workspaceCheck your understanding
You wrap an entire script file in try { ... } catch (err) { console.log('handled'); } and the file contains a misspelled keyword, functin, inside a function that is declared but never called. What happens when the page loads?
- 'handled' prints, because the try block wraps the misspelled keyword
- The engine skips the broken statement and runs the rest of the try block
- Nothing in the file runs and an uncaught SyntaxError is reported, so 'handled' never prints
- 'handled' prints only because the broken function is never called
Show answer
The engine parses the whole file, including bodies that are never called, before executing anything, so the file is rejected and the try/catch inside it never becomes running code. Option 0 is tempting because try/catch really does catch errors from the code it wraps, but that applies only to errors raised during execution; this file never reaches execution.