How to Debug JavaScript Errors Using Browser DevTools — Full Guide
Browser DevTools is the most powerful JavaScript debugging tool available — and it's already installed in every browser. This guide covers every DevTools debugging feature: the Console panel for reading errors, the Sources panel for breakpoints and call stack inspection, the Network panel for API debugging, Performance and Memory profiling, and specific workflows for the most common JavaScript error types.
F12
open DevTools in any browser on Windows/Linux
6 panels
Console, Sources, Network, Performance, Memory, Application
Breakpoints
pause execution at any line and inspect all variables
Network tab
see every API call, request, response, and timing
Opening DevTools and Navigating Panels
DevTools is built into every major browser. Chrome DevTools and Edge DevTools are identical. Firefox has equivalent features with a slightly different UI. Safari requires enabling the Develop menu first.
Opening DevTools in every browser
Open DevTools: F12 (Windows/Linux) | Cmd+Option+I (Mac) | Right-click → Inspect. Chrome and Edge have identical DevTools. Firefox has similar features with slightly different UI. Safari: Enable in Preferences → Advanced → Show Develop menu, then Develop → Show Web Inspector (Cmd+Option+I).
Console Panel — Your First Stop for Errors
The Console panel is where JavaScript errors, warnings, and your console.log output appear. Every error includes a clickable link to the exact file and line number where it occurred.
Reading error messages
Errors appear in red with a clickable file:line reference on the right. Click the file:line link to jump directly to the source code location. The error type + message tells you what happened; the file:line tells you where.
Running JS in the Console
Type any JS expression and press Enter to run it immediately on the current page. Access all global variables, call functions, inspect the DOM: document.querySelector(".my-class"). Test fixes live before editing your source code.
Filter console output
Use the filter dropdown to show only Errors, Warnings, Info, or Verbose. In large applications with lots of logging, filtering to Errors immediately cuts through the noise. You can also type in the filter box to search by message text.
Preserve log across navigations
Click the gear icon (⚙) → check "Preserve log". Keeps all console output even when the page navigates or redirects. Essential for debugging errors that happen during form submissions, login redirects, or any navigation event.
// Standard log — use for general debugging
console.log('[ComponentName]', typeof data, data);
// Table — renders arrays of objects as a formatted table
console.table(users); // Much easier than console.log for arrays of objects
// Group related logs together (collapsible in DevTools)
console.group('fetchUser request');
console.log('userId:', userId);
console.log('response:', response.status);
console.groupEnd();
// Time performance of a code section
console.time('data processing');
processLargeDataSet(data);
console.timeEnd('data processing'); // prints: "data processing: 245ms"
// Assert — only logs if condition is false
console.assert(user.id > 0, 'User ID must be positive', user);
// Log with a stack trace (where was this called from?)
console.trace('called from here');
// Error and warning (different colors/icons in DevTools)
console.error('Something failed:', error.message);
console.warn('Deprecation warning: use newMethod() instead');Sources Panel — Breakpoint Debugging
The Sources panel is the most powerful debugging tool in DevTools. Breakpoints pause execution at any line, letting you inspect all variables, step through code, and watch expressions update in real time.
// HOW TO SET A LINE BREAKPOINT:
// 1. Open Sources panel (F12 → Sources tab)
// 2. Find your file in the left sidebar, or press Ctrl+P (Cmd+P Mac) to search
// 3. Click any line number → a blue dot appears
// 4. Reload the page or trigger the code → execution pauses at that line
// 5. All variables in scope are visible in the right panel (Scope section)
// 6. Hover over any variable in the code to see its current value
// STEPPING CONTROLS (when paused at a breakpoint):
// F8 / ▶ : Resume — continue until the next breakpoint
// F10 / →: Step Over — execute this line, move to next line in same function
// F11 / ↓: Step Into — enter the function being called on this line
// Shift+F11: Step Out — finish current function, return to caller
// Ctrl+F8: Deactivate all breakpoints temporarily
// Example: debugging an async function
async function fetchUser(userId) {
// SET BREAKPOINT HERE → pauses before fetch executes
// Check: is userId what you expect?
const response = await fetch(`/api/users/${userId}`);
// SET BREAKPOINT HERE → pauses after fetch completes
// Check: response.status, response.ok
const user = await response.json();
// SET BREAKPOINT HERE → inspect the parsed user object
return user;
}
// CONDITIONAL BREAKPOINT: right-click line number → "Add conditional breakpoint"
// Only pauses when the condition is true:
// userId === undefined (debug only when userId is missing)
// items.length > 100 (debug only with large datasets)
// i === 499 (debug only at loop iteration 500)
// DOM BREAKPOINT: Elements tab → right-click an element → "Break on..."
// Pauses when JavaScript modifies the DOM element — great for "why did this change?"
// XHR/FETCH BREAKPOINT: Sources → XHR/fetch Breakpoints → click + → add URL pattern
// Pauses on any fetch/XHR matching the URL pattern (e.g., "api/users")
// EXCEPTION BREAKPOINT: Pause on exceptions button (⏸ icon in Sources)
// Pauses on any uncaught exception — shows you exactly where the error was thrownNetwork Panel — Debug API Calls
See all HTTP requests
Every request your page makes is listed: XHR/fetch for API calls, JS/CSS file loads, images. Filter by type using the toolbar buttons (XHR for API calls). Click any request to see its full details.
Check request and response details
Click a request → Headers tab: see exact request headers sent and response headers received. → Payload tab: see the request body you sent. → Response tab: see exactly what the server returned, including error responses.
Replay requests from the console
Right-click any request → "Copy as cURL" to get a cURL command that replays the exact request with all headers and body. Or "Copy as fetch" to test in the console. Essential for isolating whether a problem is in your frontend code or the server.
Throttle and simulate conditions
Click the throttle dropdown → select "Slow 3G", "Fast 3G", or "Offline". Tests how your app handles slow or no internet. Reveals loading state bugs that only appear when requests take longer than expected.
Diagnosing Specific Common Errors
// ─── TypeError: Cannot read properties of undefined ─────────────────────────
// 1. Console → click the file:line link in red error message
// 2. Sources → set breakpoint at the reported line
// 3. When paused: hover over the undefined variable, or check Scope panel (right)
// 4. The variable is null or undefined → trace back to where it should be assigned
// 5. Common causes: async data not loaded yet, API response different shape expected
// ─── Network/Fetch Error ─────────────────────────────────────────────────────
// 1. Network tab → look for the failed request (shown in red)
// 2. Check the Status code:
// 401 → authentication missing or expired
// 403 → authenticated but no permission
// 404 → URL is wrong or resource doesn't exist
// 422 → request body failed validation (check Payload tab)
// 500 → server crashed (check server logs, not browser)
// CORS → response has no CORS headers (check Console for CORS error details)
// 3. Click the request → Response tab → see what server actually returned
// (often an error message or HTML error page instead of JSON)
// ─── Async Race Condition (data sometimes undefined) ─────────────────────────
// 1. Sources → click the "Pause on exceptions" button (⏸)
// 2. Reproduce the bug → code pauses at the crash point automatically
// 3. Call Stack panel shows every function in the chain
// 4. Step up the call stack to find where data should have been set
// 5. Look for: missing await, promise not caught, state set before async resolves
// ─── "Unexpected token '<'" — API returning HTML instead of JSON ──────────────
// 1. Network tab → find the failing API request
// 2. Click request → Response tab
// 3. You'll see an HTML error page (the server is returning its error page)
// 4. Check the status code: 404 (wrong URL), 500 (server error), 302 (redirect to login)
// ─── Memory Leak (page slows over time) ──────────────────────────────────────
// 1. Memory tab → Take heap snapshot (initial state)
// 2. Use the application for a few minutes
// 3. Memory tab → Take another heap snapshot
// 4. Click "Comparison" in the dropdown → see objects that increased
// 5. Common culprits: event listeners not removed, global arrays growing, closuresApplication Panel — Debug Storage and Cookies
Inspect and edit local storage
Application → Storage → Local Storage → your domain. See all key-value pairs. Double-click any value to edit. Useful for debugging auth tokens, cached data, or feature flags stored in localStorage.
Clear cache and cookies
Application → Clear storage → check what to clear → Clear site data button. Resets the page to a fresh state. Essential for debugging caching bugs or authentication issues where old cookies interfere.
Inspect cookies
Application → Cookies → your domain. See all cookies with name, value, domain, expiry, and flags (HttpOnly, Secure, SameSite). Useful for verifying authentication cookies are being set correctly by your server.
Service worker debugging
Application → Service Workers. See registered service workers, their status, and force update or unregister them. Useful when cached service worker content is causing issues where your code changes aren't showing up.
Use the debugger; statement in your code
debugger; anywhere in your JavaScript code. When DevTools is open, execution pauses at that line exactly like a manually set breakpoint — no clicking in the UI required. Particularly useful in complex async code or callback chains where finding the exact line in Sources is difficult. Always remove debugger;statements before committing code.