{"id":"l-js-5-3","courseId":"javascript-fundamentals","lang":"en","kind":"lesson","summary":"Read error messages and stack traces, throw and catch errors on purpose with custom error types, and find bugs systematically with breakpoints and the DevTools debugger.","html":"<p>Every programmer writes bugs every day. What separates experienced developers from beginners is not fewer bugs but a calmer, faster way of finding them. That starts with reading what the computer is telling you.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Reading an error</h2>\n<p>When JavaScript cannot continue, it <strong>throws</strong> an error, and if nothing catches it, the console shows something like this:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>Uncaught TypeError: Cannot read properties of undefined (reading 'amount')\n    at totalCents (expenses.js:12:31)\n    at renderSummary (ui.js:40:17)\n    at HTMLFormElement.&lt;anonymous&gt; (main.js:27:5)</code></pre></div>\n<p>Read it in three parts. The <strong>type</strong>, <code dir=\"ltr\">TypeError</code>, says which kind of problem: a value had the wrong type for what you did to it. The <strong>message</strong> says exactly what: something before <code dir=\"ltr\">.amount</code> was <code dir=\"ltr\">undefined</code>. The <strong>stack trace</strong> lists the chain of function calls that led there, most recent first: <code dir=\"ltr\">totalCents</code> at line 12 of <code dir=\"ltr\">expenses.js</code> failed, it was called by <code dir=\"ltr\">renderSummary</code>, which was called by the form’s submit handler. In DevTools each location is a link straight to that line.</p>\n<p>The types you will meet most:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Type</th>\n<th>Usually means</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code dir=\"ltr\">ReferenceError</code></td>\n<td>A name that does not exist: typo, or used before its declaration</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">TypeError</code></td>\n<td>Wrong kind of value: calling a non-function, reading from <code dir=\"ltr\">undefined</code></td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">SyntaxError</code></td>\n<td>The code cannot be parsed: a missing bracket, quote or comma</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">RangeError</code></td>\n<td>A number outside what is allowed, such as <code dir=\"ltr\">toFixed(200)</code></td>\n</tr>\n</tbody>\n</table></div><h2 id=\"sec-2\" tabindex=\"-1\">Throwing errors on purpose</h2>\n<p>You can throw errors yourself, and you should when your code is given something it cannot sensibly handle:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"1\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> parseAmount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">text</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> dollars</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> Number</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(text);</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (text.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">trim</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ||</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> !</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">Number.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">isFinite</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(dollars) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">||</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> dollars </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x3C;=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    throw</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> new</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> Error</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`Invalid amount: \"${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">text</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}\"`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> Math.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">round</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(dollars </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 100</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">try</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">parseAmount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"12.20\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">parseAmount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"twelve\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"this line never runs\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">} </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (error) {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Caught:\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, error.message);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">} </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">finally</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"finally always runs\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p><code dir=\"ltr\">throw</code> stops the function immediately and travels up the call stack until some <code dir=\"ltr\">try</code>/<code dir=\"ltr\">catch</code> catches it. If none does, it becomes an uncaught error in the console. <code dir=\"ltr\">finally</code> runs whether or not anything was thrown, which is the place for clean-up such as hiding a loading spinner.</p>\n<h3 id=\"sec-3\" tabindex=\"-1\">Custom error types</h3>\n<p>Some errors are expected, such as a user typing a bad amount; others are bugs. Callers need to tell them apart, and a custom error class makes that possible:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"2\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">class</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> ValidationError</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> extends</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> Error</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  constructor</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">field</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">message</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">    super</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(message);</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">    this</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.name </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"ValidationError\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">    this</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.field </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> field;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> addFromForm</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">label</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">amountText</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (label.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">trim</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    throw</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> new</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> ValidationError</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"label\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Enter what you spent money on.\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { label: label.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">trim</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(), amount: Math.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">round</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">Number</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(amountText) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 100</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) };</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">try</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  addFromForm</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"  \"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"4.50\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">} </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (error) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (error </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">instanceof</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> ValidationError</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`Show next to ${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">error</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">field</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}: ${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">error</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">message</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">else</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    throw</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> error; </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// not ours to handle</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p><code dir=\"ltr\">extends Error</code> makes <code dir=\"ltr\">ValidationError</code> a kind of <code dir=\"ltr\">Error</code> with a stack trace, <code dir=\"ltr\">super(message)</code> runs the parent constructor, and the extra <code dir=\"ltr\">field</code> property tells the form where to show the message. The <code dir=\"ltr\">catch</code> handles only what it understands and <strong>rethrows</strong> everything else, so real bugs still surface.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>The empty catch</span></p>\n<p><code dir=\"ltr\">try { save(); } catch (e) {}</code> makes every failure invisible, including the bug you introduce next month. If you catch an error, do something with it: show a message, use a fallback, or at least <code dir=\"ltr\">console.error(error)</code>. Catch as close as possible to where you can actually handle the problem, and nowhere else.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">The debugger: stop and look</h2>\n<p><code dir=\"ltr\">console.log</code> is a fine first tool, but it only shows what you thought to print. The DevTools <strong>debugger</strong> pauses your program on a line and shows you everything.</p>\n<p>In Chrome or Edge, open DevTools, go to the <strong>Sources</strong> panel, open your file and click a line number. That sets a <strong>breakpoint</strong>. The next time that line is about to run, everything pauses, and you can:</p>\n<ul>\n<li>hover over any variable to see its value, or read them all in the <strong>Scope</strong> section;</li>\n<li>read the <strong>Call Stack</strong> section to see how you got there;</li>\n<li><strong>step over</strong> (run this line, pause at the next), <strong>step into</strong> (follow a function call inside), or <strong>step out</strong> (finish this function);</li>\n<li>type any expression in the Console while paused, using the paused values.</li>\n</ul>\n<p>Right-click a line number to add a <strong>conditional breakpoint</strong>, such as <code dir=\"ltr\">expense.amount &gt; 100000</code>, which pauses only when the condition is true. You can also write the statement <code dir=\"ltr\">debugger;</code> in your code: when DevTools is open, execution pauses there. Remove it before you commit.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 660 200\" role=\"img\" aria-labelledby=\"l-js-5-3-t1\"><defs><marker id=\"arrow-l-js-5-3-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-js-5-3-t1\">A cycle of five steps: reproduce the bug reliably, read the error or describe the wrong output, form a hypothesis, inspect values with logs or breakpoints, then fix and verify, and repeat if the bug remains.</title>\n  <rect class=\"d-box-accent\" x=\"10\" y=\"70\" width=\"110\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"65\" y=\"103\" text-anchor=\"middle\">Reproduce</text>\n  <rect class=\"d-box\" x=\"140\" y=\"70\" width=\"110\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"195\" y=\"103\" text-anchor=\"middle\">Read</text>\n  <rect class=\"d-box\" x=\"270\" y=\"70\" width=\"120\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"330\" y=\"103\" text-anchor=\"middle\">Hypothesise</text>\n  <rect class=\"d-box-primary\" x=\"410\" y=\"70\" width=\"110\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"465\" y=\"103\" text-anchor=\"middle\">Inspect</text>\n  <rect class=\"d-box-success\" x=\"540\" y=\"70\" width=\"110\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"595\" y=\"96\" text-anchor=\"middle\">Fix and</text>\n  <text class=\"d-label\" x=\"595\" y=\"114\" text-anchor=\"middle\">verify</text>\n  <path class=\"d-arrow\" d=\"M120 98 L136 98\" marker-end=\"url(#arrow-l-js-5-3-1)\"/>\n  <path class=\"d-arrow\" d=\"M250 98 L266 98\" marker-end=\"url(#arrow-l-js-5-3-1)\"/>\n  <path class=\"d-arrow\" d=\"M390 98 L406 98\" marker-end=\"url(#arrow-l-js-5-3-1)\"/>\n  <path class=\"d-arrow\" d=\"M520 98 L536 98\" marker-end=\"url(#arrow-l-js-5-3-1)\"/>\n  <path class=\"d-arrow\" d=\"M595 126 Q595 180 330 180 Q200 180 200 130\" marker-end=\"url(#arrow-l-js-5-3-1)\"/>\n  <text class=\"d-label-muted\" x=\"420\" y=\"172\" text-anchor=\"middle\">still broken: back to reading</text>\n</svg><figcaption>A debugging loop that works on every bug</figcaption></figure>\n<h2 id=\"sec-5\" tabindex=\"-1\">A method for any bug</h2>\n<ol>\n<li><strong>Reproduce</strong> it reliably. “Sometimes the total is wrong” becomes “after deleting the first expense, the total is wrong”.</li>\n<li><strong>Read</strong> the error, or state precisely what you expected and what you got.</li>\n<li><strong>Hypothesise</strong>: what single thing would explain it?</li>\n<li><strong>Inspect</strong> the values at the point you suspect, with a breakpoint or a log, and find the first place where reality differs from your expectation.</li>\n<li><strong>Fix</strong> the cause there, then <strong>verify</strong> with the exact steps from step 1.</li>\n</ol>\n<p>The temptation, especially when tired, is to skip straight to changing code until the symptom goes away. That usually creates a second bug. Ten minutes of looking at real values beats an hour of guessing.</p>\n<p>You now have every piece Pocket needs. Next lesson you assemble them into the finished app.</p>\n","toc":[{"id":"sec-1","text":"Reading an error","level":2},{"id":"sec-2","text":"Throwing errors on purpose","level":2},{"id":"sec-3","text":"Custom error types","level":3},{"id":"sec-4","text":"The debugger: stop and look","level":2},{"id":"sec-5","text":"A method for any bug","level":2}],"takeaways":["An error message names the error type, says what went wrong, and its stack trace points to the file and line where it happened.","Throw an <code dir=\"ltr\">Error</code> when your code cannot continue sensibly; catch it only where you can do something useful, and rethrow anything you did not expect.","A custom error class (<code dir=\"ltr\">class ValidationError extends Error</code>) lets callers tell expected problems apart from real bugs with <code dir=\"ltr\">instanceof</code>.","A breakpoint pauses your code on a line so you can inspect every variable and step through it one line at a time; <code dir=\"ltr\">debugger;</code> sets one from code.","Debug by evidence, not guesswork; reproduce the bug, find the first line where reality differs from your expectation, then fix that."],"further":[{"title":"Control flow and error handling (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Control_flow_and_error_handling"},{"title":"What went wrong? Troubleshooting JavaScript (MDN)","url":"https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/What_went_wrong"},{"title":"Error (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error"}],"quiz":[{"q":"<p><code dir=\"ltr\">TypeError: expenses.map is not a function</code> at <code dir=\"ltr\">renderExpenses (ui.js:14)</code>. What is the most useful first step?</p>","options":[{"text":"Wrap line 14 in <code dir=\"ltr\">try</code>/<code dir=\"ltr\">catch</code> so the page keeps working.","why":"That hides the symptom and leaves the cause. The list would silently stop rendering."},{"text":"Rewrite <code dir=\"ltr\">renderExpenses</code> with a <code dir=\"ltr\">for</code> loop instead of <code dir=\"ltr\">map</code>.","why":"The loop would fail too, or behave oddly. The real question is what <code dir=\"ltr\">expenses</code> actually is."},{"text":"Restart the dev server and clear the browser cache.","why":"The message describes a logic problem, a value of the wrong type, not a stale file."},{"text":"Pause at ui.js line 14 and check what <code dir=\"ltr\">expenses</code> actually holds, then trace back to where that value came from.","why":"Correct. <code dir=\"ltr\">map</code> is missing, so <code dir=\"ltr\">expenses</code> is not an array, perhaps <code dir=\"ltr\">null</code> from storage or an object. Find out which, then fix the source."}],"answer":3},{"q":"<p>A <code dir=\"ltr\">catch</code> block in Pocket’s save function reads <code dir=\"ltr\">catch (error) {}</code>. What is the problem?</p>","options":[{"text":"Any failure, including a typo-level bug in your own code, now disappears without a trace.","why":"Correct. An empty catch swallows every error. Handle what you expect, log or rethrow the rest."},{"text":"The <code dir=\"ltr\">error</code> parameter must always be used or the code will not run.","why":"An unused parameter is legal. You can even write <code dir=\"ltr\">catch {}</code>. The issue is silently discarding errors."},{"text":"<code dir=\"ltr\">catch</code> blocks only work inside <code dir=\"ltr\">async</code> functions.","why":"<code dir=\"ltr\">try</code>/<code dir=\"ltr\">catch</code> works in any function. With async code you just have to <code dir=\"ltr\">await</code> inside the <code dir=\"ltr\">try</code>."}],"answer":0},{"q":"<p>You want to know why a total is wrong only for the 40th expense in a loop. Which DevTools feature fits best?</p>","options":[{"text":"A <code dir=\"ltr\">console.log</code> before the loop.","why":"That runs once, before the interesting part. It cannot show you the 40th pass."},{"text":"The Network panel.","why":"The Network panel shows requests to servers. This is a calculation inside your own code."},{"text":"A conditional breakpoint inside the loop, such as <code dir=\"ltr\">i === 39</code>.","why":"Correct. The debugger pauses only on that pass, and you can inspect every variable at that moment."},{"text":"Reloading the page with the cache disabled.","why":"Caching does not change how a calculation runs. You need to look at the values during the loop."}],"answer":2}],"exercise":{"kind":"web","mode":"js","starter":{"js":"// 1. An error type for problems the user can fix\nclass ValidationError extends Error {\n  constructor(field, message) {\n    super(message);\n    // set name and field here\n  }\n}\n\n// 2. Return whole cents, or throw a ValidationError for the \"amount\" field\nfunction parseAmount(text) {\n  return Math.round(Number(text) * 100);\n}\n\n// 3. Return { ok: true, list } with the new expense added, or\n//    { ok: false, field, message } for a ValidationError.\n//    Any other error must NOT be swallowed.\nfunction tryAdd(list, label, amountText) {\n  try {\n    const expense = { label: label.trim(), amount: parseAmount(amountText) };\n    return { ok: true, list: [...list, expense] };\n  } catch (error) {\n    return { ok: false };\n  }\n}\n\nconsole.log(tryAdd([], \"Tea\", \"abc\"));\n"},"solution":{"js":"class ValidationError extends Error {\n  constructor(field, message) {\n    super(message);\n    this.name = \"ValidationError\";\n    this.field = field;\n  }\n}\n\nfunction parseAmount(text) {\n  const dollars = Number(text);\n  if (text.trim() === \"\" || !Number.isFinite(dollars) || dollars <= 0) {\n    throw new ValidationError(\"amount\", \"Enter an amount greater than zero.\");\n  }\n  return Math.round(dollars * 100);\n}\n\nfunction tryAdd(list, label, amountText) {\n  try {\n    if (label.trim() === \"\") {\n      throw new ValidationError(\"label\", \"Enter what you spent money on.\");\n    }\n    const expense = { label: label.trim(), amount: parseAmount(amountText) };\n    return { ok: true, list: [...list, expense] };\n  } catch (error) {\n    if (error instanceof ValidationError) {\n      return { ok: false, field: error.field, message: error.message };\n    }\n    throw error;\n  }\n}\n\nconsole.log(tryAdd([], \"Tea\", \"abc\"));\n"},"checks":[{"id":"error-class","test":"const e = new ValidationError('amount', 'Bad');\nreturn e instanceof Error && e.name === 'ValidationError' && e.field === 'amount' && e.message === 'Bad';\n"},{"id":"parse-ok","test":"return parseAmount('4.10') === 410 && parseAmount(' 12 ') === 1200;\n"},{"id":"parse-throws","test":"const bad = ['', '   ', 'abc', '0', '-5', 'Infinity'];\nreturn bad.every((t) => { try { parseAmount(t); return false; } catch (e) { return e instanceof ValidationError && e.field === 'amount'; } });\n"},{"id":"try-add-ok","test":"const r = tryAdd([{ label: 'Bus', amount: 270 }], '  Tea ', '3');\nreturn r.ok === true && r.list.length === 2 && r.list[1].label === 'Tea' && r.list[1].amount === 300;\n"},{"id":"try-add-invalid","test":"const a = tryAdd([], 'Tea', 'abc');\nconst b = tryAdd([], '  ', '3');\nreturn a.ok === false && a.field === 'amount' && typeof a.message === 'string' && a.message.length > 0 && b.ok === false && b.field === 'label';\n"},{"id":"rethrows","test":"try { tryAdd(null, 'Tea', '3'); return false; } catch (e) { return e instanceof TypeError; }\n"}],"title":"Throw what users can fix, rethrow the rest","prompt":"<ol>\n<li>Finish <code dir=\"ltr\">ValidationError</code> so instances have <code dir=\"ltr\">name</code> set to <code dir=\"ltr\">&quot;ValidationError&quot;</code> and a <code dir=\"ltr\">field</code> property.</li>\n<li>Make <code dir=\"ltr\">parseAmount(text)</code> return whole cents, or throw a <code dir=\"ltr\">ValidationError</code> with field <code dir=\"ltr\">&quot;amount&quot;</code> when the text is empty, not a finite number, or not greater than 0.</li>\n<li>Fix <code dir=\"ltr\">tryAdd(list, label, amountText)</code>. For a valid entry it returns <code dir=\"ltr\">{ ok: true, list }</code> with the new expense (trimmed label, amount in cents) added. An empty label throws a <code dir=\"ltr\">ValidationError</code> with field <code dir=\"ltr\">&quot;label&quot;</code>. Validation errors become <code dir=\"ltr\">{ ok: false, field, message }</code>. <strong>Any other error must be rethrown</strong>, not swallowed.</li>\n</ol>\n","hints":["In the constructor, after <code dir=\"ltr\">super(message)</code>: <code dir=\"ltr\">this.name = &quot;ValidationError&quot;; this.field = field;</code>.","<code dir=\"ltr\">Number(&quot;&quot;)</code> is 0 and <code dir=\"ltr\">Number(&quot;Infinity&quot;)</code> is Infinity, so check <code dir=\"ltr\">text.trim() === &quot;&quot;</code> and <code dir=\"ltr\">Number.isFinite(dollars) &amp;&amp; dollars &gt; 0</code>.","In the <code dir=\"ltr\">catch</code>: <code dir=\"ltr\">if (error instanceof ValidationError) { return { ok: false, field: error.field, message: error.message }; } throw error;</code>."],"explanation":"<p><code dir=\"ltr\">ValidationError</code> extends <code dir=\"ltr\">Error</code>, so it keeps a message and a stack trace, and adds the <code dir=\"ltr\">field</code> the form needs to place the message. <code dir=\"ltr\">parseAmount</code> throws instead of returning a special value, so callers cannot accidentally treat a bad amount as a number. In <code dir=\"ltr\">tryAdd</code>, the <code dir=\"ltr\">catch</code> checks <code dir=\"ltr\">instanceof ValidationError</code> and turns only those into a friendly result; anything else, such as the <code dir=\"ltr\">TypeError</code> from spreading <code dir=\"ltr\">null</code>, is a bug in the calling code and is rethrown so it shows up in the console instead of disappearing.</p>\n","checkLabels":{"error-class":"<code dir=\"ltr\">ValidationError</code> is an Error with a name and a field","parse-ok":"<code dir=\"ltr\">parseAmount</code> returns whole cents for valid text","parse-throws":"<code dir=\"ltr\">parseAmount</code> throws a ValidationError for empty, non-numeric, zero, negative or infinite input","try-add-ok":"<code dir=\"ltr\">tryAdd</code> adds a valid expense","try-add-invalid":"<code dir=\"ltr\">tryAdd</code> returns { ok: false, field, message } for invalid input","rethrows":"<code dir=\"ltr\">tryAdd</code> rethrows errors that are not ValidationErrors"}},"words":717,"readMinutes":3}