{"record":{"id":"a7ba763dbec602c9","repo":"gitbutlerapp/gitbutler","slug":"error-triggering-button-click-via-hotkey","errorCode":null,"errorMessage":"Error triggering button click via hotkey:","messagePattern":"Error triggering button click via hotkey:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/ui/src/lib/focus/focusManager.ts","lineNumber":144,"sourceCode":"\n\t\t// Find all buttons with hotkeys\n\t\tconst entries = Array.from(this.nodeMap.entries());\n\t\tfor (const [element, node] of entries) {\n\t\t\tif (node.options.button && node.options.hotkey) {\n\t\t\t\t// Parse the hotkey definition\n\t\t\t\tconst parsed = parseHotkey(node.options.hotkey);\n\t\t\t\tif (!parsed) continue;\n\n\t\t\t\t// Check if the event matches the hotkey\n\t\t\t\tif (matchesHotkey(event, parsed)) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tevent.stopPropagation();\n\n\t\t\t\t\t// Trigger click on the button\n\t\t\t\t\ttry {\n\t\t\t\t\t\telement.click();\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.warn(\"Error triggering button click via hotkey:\", error);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t// ============================================\n\t// Public API\n\t// ============================================\n\n\tlisten() {\n\t\treturn mergeUnlisten(\n\t\t\ton(document, \"click\", this.handleMouse, { capture: true }),\n\t\t\ton(document, \"keydown\", this.handleKeys),\n\t\t);\n\t}","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/packages/ui/src/lib/focus/focusManager.ts#L126-L162","documentation":"The focus manager maps hotkeys to registered focusable buttons; on a match it calls element.click() synchronously inside try/catch. HTMLElement.click() itself essentially never throws — an exception here means the button's own click handler (or a synchronous listener on it) threw during dispatch. The catch warns, and the hotkey is still reported handled.","triggerScenarios":"Pressing a registered hotkey whose bound button's click handler throws — handler code with a bug, a listener on a detached element, or stale registry entries after DOM updates (packages/ui/src/lib/focus/focusManager.ts:144).","commonSituations":"A button handler failing only on the hotkey path (different event shape); focus registry entries not unregistered on unmount; third-party listeners throwing.","solutions":["Click the same button with the mouse to reproduce — the real stack belongs to the handler, not focusManager","Fix the throwing click handler; the warn here is a symptom","Unregister hotkey nodes on unmount so stale elements are never clicked","Make async handler work catch its own rejections so nothing escapes synchronously"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Only click live, connected buttons\nif (!(element instanceof HTMLElement) || !element.isConnected) continue;","typeGuard":"function isLiveButton(el: unknown): el is HTMLButtonElement {\n\treturn el instanceof HTMLButtonElement && el.isConnected;\n}","tryCatchPattern":"try {\n\telement.click();\n} catch (error) {\n\tconsole.warn(\"Error triggering button click via hotkey:\", error);\n}\nreturn true;","preventionTips":["Unregister focus and hotkey entries when nodes unmount","Keep hotkey-triggered handlers defensive about event shape (no MouseEvent on the hotkey path)","Test each registered hotkey in CI where feasible"],"tags":["typescript","dom","hotkey","focus","event-handling"],"backgroundTag":"dom-event-handler-error","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}