Reading Errors and Using the Debugger
Sooner or later, usually sooner, a script stops doing what you meant and the Console turns red. That red line is not a verdict on you. It is the program telling you what went wrong and where it noticed. Learning to read errors early saves more time than any other habit in this book, because every error you can read is a problem you can fix without guessing.
An error message is like a smoke alarm that also tells you which room the smoke is in. It is loud and never welcome, but it is far better than silence, and it tells you where to start looking. A script that fails silently would leave you searching the whole house.
Where Do Errors Appear?
Errors appear in the Console as red lines. Each one starts with the error's name, such as TypeError, followed by a message in plain words. On the right is the file and line number where the program noticed the problem, such as main.js, line 4. Clicking it opens the file in DevTools with that line highlighted. When the Console is closed, a small red counter at the top of DevTools shows how many errors are waiting.
An error stops the rest of that script: the lines after it never run. Other scripts on the page carry on, and the HTML and CSS are not affected, which is one more reason the content belongs in the HTML. The visitor sees no message at all, only that something did not happen. So keep the Console open while you work; it is the only place a script can complain.
Three Errors You Will Meet
A SyntaxError means the code cannot even be read. Syntax is the grammar of a language, and a missing bracket or quote breaks it. The browser checks the grammar of the whole file before running any of it, so a syntax error stops every line in the file, even the ones above the mistake.
console.log("Starting"); console.log("Loaded projects: 3";
Uncaught SyntaxError: missing ) after argument list (at main.js:2:13)
The second line has lost its closing round bracket. The Console does not show Starting, even though that line is perfectly fine, because nothing in the file ran. Read aloud, the message says: syntax error, missing closing bracket after argument list. The argument list is the values inside a call's brackets, so the message says exactly what to add, and the brackets at the end say roughly where: main.js, line 2.
A ReferenceError means the code uses a name that does not exist. Usually it is a typo, sometimes a name used outside its scope, as in the previous topic.
const projectCount = 3;
console.log(projectCont);
Uncaught ReferenceError: projectCont is not defined
at main.js:2:13
The message reads: reference error, projectCont is not defined. The name was made as projectCount, with a u, and used without it. JavaScript does not guess that you meant the name next door; every letter, and every capital, has to match.
A TypeError means a value is being used in a way its type does not allow. By far the most common kind is reading a property, a named value that belongs to another value, from something that is undefined or null. The toFixed method of the previous topic was one: you reach a property with a dot.
let visitorName;
console.log(visitorName.toUpperCase());
Uncaught TypeError: Cannot read properties of undefined (reading 'toUpperCase')
at main.js:2:25
A string's toUpperCase method gives its text in capitals, but visitorName was made with let and never given a value, so it is undefined, and undefined has no properties at all. The message says: type error, cannot read properties of undefined, reading toUpperCase. The same message with null in place of undefined is probably the most common error on the web; Chapter 8 shows where it usually comes from. The fix is to give the name a value before using it, or to check it first.
The Stack: How Did We Get Here?
Under most error messages, the Console adds a list called the stack, or call stack: the chain of function calls that were running when the error happened, the most recent at the top. Under the last two errors it had one line, starting with at, because the mistake was in the script itself and not inside any function. Earlier topics showed only the first line of each error; the lines under it were this list. When the error happens inside a function, the list grows. Read it from the top down: where it broke, then who called that, then who called that.
function shout(text) { return text.toUpperCase(); } function welcome(visitor) { return shout(visitor); } let visitorName; console.log(welcome(visitorName));
Uncaught TypeError: Cannot read properties of undefined (reading 'toUpperCase')
at shout (main.js:2:15)
at welcome (main.js:6:10)
at main.js:10:13
The top line of the stack says the error happened in shout, at line 2. The next says shout was called by welcome, at line 6. The last says welcome was called from line 10 of the script itself. The number after the second colon is the column, how many characters along the line. Chrome shows each file name as a link.
Notice where the mistake really is. shout did exactly what it was asked; so did welcome. The cause is line 9, where visitorName was made without a value. The line number in an error is where the program noticed the problem, and the stack is the trail you follow back to where it started.
Pausing the Script: Breakpoints
You could add a console.log for every value you are unsure of, and sometimes that is the quickest way. The debugger, the part of DevTools built for finding bugs, does better. A breakpoint is a marker on a line that tells the browser to pause the script when it reaches that line, before running it. It shares only its name with the width breakpoints of Chapter 5. While the script is paused, you can look at every value it has.
In DevTools, open the Sources panel, find main.js in the list of files on the left, and click the number of line 6. A blue marker appears on it. Reload the page. The script stops right before line 6 runs, the line is highlighted, and the page waits.
Now look around. Hover over any name in the code to see its value. The Scope section on the right lists every name the paused code can see; here it shows visitor as undefined, which is the whole bug found in one glance. The Step over button runs one line and pauses again, so you can watch values change line by line. The Resume button lets the script carry on. Click the blue marker again to remove it.
You can also write the keyword debugger, followed by a semicolon, on a line of your own code. When DevTools is open, the script pauses there exactly as at a breakpoint; when it is closed, the line does nothing. Delete it before you publish.
A Method for Any Bug
Four steps work for almost every bug. First, make it happen again on purpose, so you know how to test a fix. Second, read the whole message: the type, the words, the line, the stack. Third, check the value you assumed. Most bugs are a value that is not what you thought it was, and a breakpoint or a single log shows it. Fourth, change one thing, then test again. Changing three things at once can fix the bug and leave you not knowing which change did it.
Professional developers see errors all day. What makes them fast is not avoiding errors but reading them: the name says what kind of problem, the message says what, the stack says where to look.
- "An error means I am bad at this." Errors are the normal state of code being written. Reading them quickly is the skill, and it comes from practice, not talent.
- "The line number shows where the mistake is." It shows where the program noticed. The cause is often a line or two earlier, or in the code that called this one.
- "Putting
console.logeverywhere is the only way to debug." A breakpoint shows every value at once, without editing the code, and lets you step through it line by line.
- From here on, an error is information you can act on instead of a dead end.
- The "cannot read properties of null" error returns in Chapter 8, and by then you will know how to read it.
Knowledge Check
You made a name called total and later typed totl by mistake. Which error appears?
- A
SyntaxError, because the code is broken - A
ReferenceError: the name is not defined - A
TypeError, because the type is wrong - No error, as JavaScript uses the closest name
What does the stack under an error message show?
- The changes you made to the file since it last worked
- Every error the page has produced since it loaded
- The chain of function calls that led to the error
- The value of every name at the moment of the error
What can you do while a script is paused at a breakpoint?
- See every value and step through the code line by line
- Have DevTools rewrite the broken line with the fix
- Undo the last line the script ran and run it again
- Show the visitor a message explaining the problem
A file has a SyntaxError on its last line. What happens to the lines above it?
- They run as usual, and then the script stops
- They run, and the browser skips the broken line
- They run twice, once before the error is shown
- None of them run, not even the very first line
You got correct