{"record":{"id":"c69835eb5d9f5274","repo":"wailsapp/wails","slug":"getclientrect-d-failed","errorCode":null,"errorMessage":"GetClientRect(%d) failed","messagePattern":"GetClientRect\\((.+?)\\) failed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/pkg/w32/user32.go","lineNumber":766,"sourceCode":"}\n\nfunc InvalidateRect(hwnd HWND, rect *RECT, erase bool) bool {\n\tret, _, _ := procInvalidateRect.Call(\n\t\tuintptr(hwnd),\n\t\tuintptr(unsafe.Pointer(rect)),\n\t\tuintptr(BoolToBOOL(erase)))\n\n\treturn ret != 0\n}\n\nfunc GetClientRect(hwnd HWND) *RECT {\n\tvar rect RECT\n\tret, _, _ := procGetClientRect.Call(\n\t\tuintptr(hwnd),\n\t\tuintptr(unsafe.Pointer(&rect)))\n\n\tif ret == 0 {\n\t\tpanic(fmt.Sprintf(\"GetClientRect(%d) failed\", hwnd))\n\t}\n\n\treturn &rect\n}\n\nfunc GetDC(hwnd HWND) HDC {\n\tret, _, _ := procGetDC.Call(\n\t\tuintptr(hwnd))\n\n\treturn HDC(ret)\n}\n\nfunc ReleaseDC(hwnd HWND, hDC HDC) bool {\n\tret, _, _ := procReleaseDC.Call(\n\t\tuintptr(hwnd),\n\t\tuintptr(hDC))\n\n\treturn ret != 0","sourceCodeStart":748,"sourceCodeEnd":784,"githubUrl":"https://github.com/wailsapp/wails/blob/0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3/v3/pkg/w32/user32.go#L748-L784","documentation":"GetClientRect returned FALSE, so the wrapper panics. The Win32 API fails when the given HWND is not a valid window — destroyed, not yet fully created, belongs to another process, or is a garbage handle. The panic message includes the numeric HWND to identify which handle failed.","triggerScenarios":"Calling GetClientRect on a window after WM_DESTROY/CloseHandle; using an HWND captured before window creation completed; passing 0 or an HWND from a different thread's window during teardown; calling after the native window was destroyed because the browser process crashed.","commonSituations":"Wails v3 Windows window-events code reading the client rect during shutdown or in a resize callback racing destruction; retry/recreate-window logic that keeps stale HWNDs; dialogs whose owner window was closed by the user.","solutions":["Guard the call with w32.IsWindow(hwnd) (or check hwnd != 0) before measuring","Stop layout/resize goroutines when the window-destroyed event arrives, before the HWND becomes invalid","Re-fetch HWNDs from the live window object instead of caching them across lifecycle events","Recover in UI helpers so a shutdown race degrades to a skipped layout pass"],"exampleFix":"// before\nrect := w32.GetClientRect(hwnd) // panics during teardown\n\n// after\nif hwnd == 0 || !w32.IsWindow(hwnd) {\n    return // window destroyed; skip measurement\n}\nrect := w32.GetClientRect(hwnd)","handlingStrategy":"validation","validationCode":"if hwnd == 0 || !w32.IsWindow(hwnd) {\n    return nil, errors.New(\"GetClientRect: invalid window handle\")\n}\nrect := w32.GetClientRect(hwnd)","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        rect = nil // window died mid-measurement; skip this pass\n    }\n}()","preventionTips":["Stop measurement goroutines on window-destroy events","Never cache HWNDs across window recreation","Check IsWindow before any HWND-taking call during shutdown paths"],"tags":["windows","win32","hwnd","lifecycle","panic"],"backgroundTag":null,"analyzedSha":"0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3","analyzedAt":"2026-08-15T14:17:36.034Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}