2.5 KiB
2.5 KiB
Silent setTimeout / Async Callback Errors
Session Context
Debugged in ShopProQuote (PocketBase web app) — "Mark as Completed" flow on repair orders.
The Bug
Clicking work-status-badge → "Completed" → confirm modal → nothing. Financial summary modal never appeared. No console errors.
The Chain
- User clicks confirm button in dynamically-created confirm modal element
- Click handler calls
updateRepairOrderWorkStatus(roId, 'completed')(async) - Inside, code reaches
if (newWorkStatus === 'completed')branch (confirmed by console.log) setTimeout(() => { openFinancialSummaryModal(roId); }, 100)is scheduledreturnexits both the function and the enclosingtry/catch- 100ms later,
openFinancialSummaryModalruns — this is NOW outside any try/catch - Line 1545:
document.getElementById('financial-customer-name').textContent = ... #financial-customer-namenever existed in the HTML →null.textContent→TypeError- The TypeError is uncaught (no try/catch in the detached context) → global error handler
- Global error handler does nothing visible → user sees "nothing happens"
The Tool That Caught It
Overriding window.setTimeout from Playwright:
page.evaluate("""()=>{
window.__origSetTimeout = window.setTimeout.bind(window);
window.setTimeout = function(fn, delay) {
if(delay===100) {
return window.__origSetTimeout(function(){
try { return fn(); }
catch(e) { console.error('TIMEOUT ERROR:', e); }
}, delay);
}
return window.__origSetTimeout(fn, delay);
};
}""")
This logged: TIMEOUT ERROR: TypeError: Cannot set properties of null (setting 'textContent') at openFinancialSummaryModal (repair-orders.js:1545:72)
The Fix
Added missing <span id="financial-customer-name">--</span> to the financial-summary-modal HTML in repair-orders.html.
Verification Pattern
After the fix, the full keyboard+click flow was re-tested end-to-end via Playwright:
- Create repair order (fill form)
- Click work status badge → select "Completed"
- Confirm modal → financial modal appears
- Fill CP Total / CP Cost
- Click "Complete" → modal closes, notification shows
One way to overwrite for robust test runs:
# Trap ALL setTimeout errors during test
page.evaluate("""()=>{
const _st=window.setTimeout.bind(window);
window.setTimeout=(fn,d)=>_st(function(){
try{return fn()}catch(e){console.error('setTimeout ERROR:',e.stack||e)}
},d);
}""")