{"record":{"id":"3bfa15171e4e44fe","repo":"wailsapp/wails","slug":"callback-callbackid-not-registered","errorCode":null,"errorMessage":"Callback '${callbackID}' not registered!!!","messagePattern":"Callback '(.+?)' not registered!!!","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v2/internal/frontend/runtime/desktop/calls.js","lineNumber":174,"sourceCode":" * @export\n * @param {string} incomingMessage\n */\nexport function Callback(incomingMessage) {\n\t// Parse the message\n\tlet message;\n\ttry {\n\t\tmessage = JSON.parse(incomingMessage);\n\t} catch (e) {\n\t\tconst error = `Invalid JSON passed to callback: ${e.message}. Message: ${incomingMessage}`;\n\t\truntime.LogDebug(error);\n\t\tthrow new Error(error);\n\t}\n\tlet callbackID = message.callbackid;\n\tlet callbackData = callbacks[callbackID];\n\tif (!callbackData) {\n\t\tconst error = `Callback '${callbackID}' not registered!!!`;\n\t\tconsole.error(error); // eslint-disable-line\n\t\tthrow new Error(error);\n\t}\n\tclearTimeout(callbackData.timeoutHandle);\n\n\tdelete callbacks[callbackID];\n\n\tif (message.error) {\n\t\tconst err = message.error instanceof Error ? message.error : new Error(message.error);\n\t\tcallbackData.reject(err);\n\t} else {\n\t\tcallbackData.resolve(message.result);\n\t}\n}\n","sourceCodeStart":156,"sourceCodeEnd":187,"githubUrl":"https://github.com/wailsapp/wails/blob/0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3/v2/internal/frontend/runtime/desktop/calls.js#L156-L187","documentation":"CreateCompatibleDC wraps gdi32.CreateCompatibleDC, which creates a memory device context matching a given DC (or the screen when passed 0). The w32 wrapper panics with 'Create compatible DC failed' when the API returns NULL, meaning GDI could not allocate a new DC. This most commonly happens under GDI handle exhaustion (10,000 GDI objects per process default) or when the source HDC parameter is itself invalid.","triggerScenarios":"Creating memory DCs in a loop without calling DeleteDC on each one, leaking GDI handles until the process hits the GDI object cap; passing an HDC obtained from a destroyed window or an already-deleted DC as the source; running many offscreen-buffer allocations concurrently during heavy animation or screenshot loops.","commonSituations":"Rendering thumbnails or frames into compatible bitmaps at high frequency with a missing DeleteDC in an error path; long-running Wails apps on Windows whose GDI handle count slowly climbs (visible in Task Manager's 'GDI objects' column); the leak only manifests as this panic hours into a session.","solutions":["Audit every CreateCompatibleDC call site and guarantee DeleteDC is called on all paths, including errors (use defer immediately after creation succeeds).","Watch the process GDI object count in Task Manager / Process Explorer while reproducing; a steady climb pinpoints the leaking code path.","Cache and reuse a single memory DC per window instead of per-frame creation.","Check the source HDC passed in is non-zero and alive; pass 0 to create a screen-compatible DC when no source is needed.","If the cap is genuinely reached by design, raise the GDI process limit via SetProcessGDIObjectCount or the registry GDIProcessHandleQuota, but treat that as a last resort after fixing leaks."],"exampleFix":"// before\nhdcMem := w32.CreateCompatibleDC(hdc)\nimg := renderFrame(hdcMem)\n// no DeleteDC on the error path -> GDI handle leak -> eventual panic\n\n// after\nhdcMem := w32.CreateCompatibleDC(hdc)\ndefer w32.DeleteDC(hdcMem)\nimg := renderFrame(hdcMem)","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"func newMemDC(hdc w32.HDC) (w32.HDC, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"CreateCompatibleDC: %v (GDI objects: check Task Manager)\", r)\n\t\t}\n\t}()\n\treturn w32.CreateCompatibleDC(hdc), nil\n}","preventionTips":["Pair every CreateCompatibleDC with an immediate defer DeleteDC.","Cache one memory DC per window instead of allocating per frame.","Track GDI object count during development to catch leaks long before the 10,000 cap."],"tags":["windows","gdi","handle-leak","syscall","panic"],"backgroundTag":null,"analyzedSha":"0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3","analyzedAt":"2026-08-15T14:17:36.034Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}