Errors, Debugging and Security · Lesson 20 of 25
Debugging LWC JavaScript in Chrome DevTools
Sprinkling console.log everywhere and redeploying works – slowly. A few DevTools skills make LWC debugging dramatically faster: readable source code, breakpoints, a handful of console methods most people never use, and knowing what the common error messages actually mean.
In this lesson
- Turning on debug mode
- Finding your component's source and setting breakpoints
- Console methods beyond console.log
- Reading LWC proxies
- Common errors and what they mean
Step 1: debug mode
In production mode Salesforce serves minified JavaScript – single-letter variables, one giant line. Enable debug mode for your user (Setup → Debug Mode → select the user → Enable) and you get your original, readable code plus extra framework warnings. Pages load slower in debug mode, so only enable it for developers, and switch it off when you're done.
Step 2: breakpoints
Pausing inside your component
- Open DevToolsF12 or Cmd+Option+I, then the Sources panel
- Find the fileCmd/Ctrl+P and type the component name, e.g. accountSearch.js (under modules/c/)
- Set a breakpointclick a line number; use a conditional breakpoint to pause only for one record
- Trigger itclick in the UI; inspect variables, this, the call stack; step through
You can also put a debugger; statement in your code: when DevTools is open, execution pauses there. Remove it before committing – the LWC ESLint config flags it.
handleSave() {
const fields = this.collectFields();
debugger; // pauses here while DevTools is open
this.save(fields);
}
Useful extras: "Pause on caught exceptions" finds errors that a catch block quietly swallowed, and logpoints (right-click a line number → Add logpoint) log a value without changing or redeploying code.
Console methods worth knowing
const accounts = [
{ Name: 'Acme', Industry: 'Energy', Rating: 'Hot' },
{ Name: 'Globex', Industry: 'Retail', Rating: 'Warm' },
];
console.table(accounts); // arrays of records as a table
console.group('Save flow'); // indent related messages
console.log('validating 2 records');
console.log('calling Apex');
console.groupEnd();
for (const a of accounts) console.count(a.Rating === 'Hot' ? 'hot' : 'other');
console.assert(accounts.length === 3, 'expected 3 accounts, got', accounts.length); // logs only when false
console.log({ accountCount: accounts.length }); // wrap in {} to print the variable name too
┌─────────┬──────────┬──────────┬────────┐
│ (index) │ Name │ Industry │ Rating │
├─────────┼──────────┼──────────┼────────┤
│ 0 │ 'Acme' │ 'Energy' │ 'Hot' │
│ 1 │ 'Globex' │ 'Retail' │ 'Warm' │
└─────────┴──────────┴──────────┴────────┘
Save flow
validating 2 records
calling Apex
hot: 1
other: 1
Assertion failed: expected 3 accounts, got 2
{ accountCount: 2 }
Also handy: console.time('load') / console.timeEnd('load') to measure how long something takes, and console.trace() to see which code called a function.
Reading LWC proxies
Log a wire result or an @api object and the console shows Proxy {}. You have three options:
console.log(JSON.stringify(this.records, null, 2))– quick and always works.console.log(JSON.parse(JSON.stringify(this.records)))– a plain copy you can expand in the console.- In DevTools settings, enable Custom formatters. In debug mode LWC uses them to show the real values inside proxies.
Common errors and what they mean
| Message | Usual cause |
|---|---|
| Cannot read properties of undefined (reading 'x') | data not loaded yet, a blank field, or this lost in a callback – use ?. and arrow functions |
| 'set' on proxy: trap returned falsish / Invalid mutation | changing @wire or @api data – copy it first |
| Cannot assign to read only property | sorting or editing a frozen array in place – use toSorted or spread |
| [LWC warn] … not reactive / template not updating | mutating inside an object without @track – assign a new value |
| Uncaught (in promise) | a rejected promise with no catch – add try/catch around await |
| Maximum call stack size exceeded | a getter or renderedCallback that sets state and re-triggers itself |
Test the logic outside the org
The fastest debugging loop doesn't involve the browser at all. Keep logic in pure functions and cover them with Jest tests (see Jest unit testing) – a failing test points straight at the line, in seconds, without deploying.
Practice
Try it yourself
A save fails silently for one specific record only. What DevTools feature lets you stop exactly there?
Show answer
A conditional breakpoint in the save method, e.g.
this.recordId === '006XX0000001', combined with "Pause on caught exceptions" in case the error is swallowed.In production the stack trace says
at e.t (c.js:1:2381). How do you get a readable one?Show answer
Enable debug mode for your user and reproduce the error – the code is served unminified with real names and lines.
Quick check
Why enable debug mode?
To get unminified source code and extra framework warnings.
What does console.assert print when the condition is true?
Nothing – it only logs when the condition is false.
How do you see the values inside an LWC proxy?
JSON.stringify it, or enable custom formatters in debug mode.
Summary
- Enable debug mode for readable code; disable it afterwards.
- Use breakpoints, conditional breakpoints, logpoints and debugger;.
- console.table, group, count and assert make logs readable.
- Learn the common error messages – most point to null data, read-only data or lost this.
Finished this lesson?
Mark it complete to track your progress, then carry on with the next lesson.