{"record":{"id":"4ff471e031566e1d","repo":"wailsapp/wails","slug":"cancellablepromise-prototype-finally-called-on-an","errorCode":null,"errorMessage":"CancellablePromise.prototype.finally called on an invalid object.","messagePattern":"CancellablePromise\\.prototype\\.finally called on an invalid object\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"v3/internal/runtime/desktop/@wailsio/runtime/src/cancellable.ts","lineNumber":487,"sourceCode":"     * This method is implemented in terms of {@link then} and the same caveats apply.\n     * It is polyfilled, hence available in every OS/webview version.\n     *\n     * @returns A Promise for the completion of the callback.\n     * Cancellation requests on the returned promise\n     * will propagate up the chain to the parent promise,\n     * but not in the other direction.\n     *\n     * The promise returned from {@link cancel} will fulfill only after all attached handlers\n     * up the entire promise chain have been run.\n     *\n     * If `onfinally` returns a cancellable promise,\n     * cancellation requests will be diverted to it,\n     * and the specified `oncancelled` callback will be discarded.\n     * See {@link then} for more details.\n     */\n    finally(onfinally?: (() => void) | undefined | null, oncancelled?: CancellablePromiseCanceller): CancellablePromise<T> {\n        if (!(this instanceof CancellablePromise)) {\n            throw new TypeError(\"CancellablePromise.prototype.finally called on an invalid object.\");\n        }\n\n        if (!isCallable(onfinally)) {\n            return this.then(onfinally, onfinally, oncancelled);\n        }\n\n        return this.then(\n            (value) => CancellablePromise.resolve(onfinally()).then(() => value),\n            (reason?) => CancellablePromise.resolve(onfinally()).then(() => { throw reason; }),\n            oncancelled,\n        );\n    }\n\n    /**\n     * We use the `[Symbol.species]` static property, if available,\n     * to disable the built-in automatic subclassing features from {@link Promise}.\n     * It is critical for performance reasons that extenders do not override this.\n     * Once the proposal at https://github.com/tc39/proposal-rm-builtin-subclassing","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/wailsapp/wails/blob/0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3/v3/internal/runtime/desktop/@wailsio/runtime/src/cancellable.ts#L469-L505","documentation":"SetBkColor sets the background color used for text and hatched fills on a DC, returning the previous color. The w32 wrapper panics with 'SetBkColor failed' when the API returns CLR_INVALID, its documented failure code. As with SetTextColor, an invalid/destroyed HDC is the dominant real-world cause; the color argument accepts essentially any COLORREF so it rarely triggers the failure.","triggerScenarios":"Using an HDC after its owning paint cycle completed (post-EndPaint); calling with a DC whose window was destroyed; inter-leaving w32 GDI calls with a framework that already released the DC it handed you.","commonSituations":"Opaque text drawing (SetBkMode(OPAQUE) + SetBkColor) performed in a deferred callback that runs after WM_PAINT returns; double-release of a DC in cleanup code.","solutions":["Scope SetBkColor to the BeginPaint/EndPaint or GetDC/ReleaseDC block that owns the HDC.","Check for double ReleaseDC/EndPaint — the second release invalidates the handle used later.","Prefer setting background color right before the text call that needs it, not in long-lived setup, so lifetime is obvious.","Reproduce with a debug breakpoint on the panic and inspect whether the HDC value matches the one returned by BeginPaint."],"exampleFix":"// before\nfunc onPaint(hdc w32.HDC){ setColorsAsync(hdc) } // async runs after EndPaint\n\n// after\nfunc onPaint(hdc w32.HDC){\n\tw32.SetBkMode(hdc, w32.OPAQUE)\n\tw32.SetBkColor(hdc, bgColor)\n\tdrawTextNow(hdc)\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"defer func() {\n\tif r := recover(); r != nil {\n\t\tif msg, _ := r.(string); strings.HasPrefix(msg, \"SetBkColor failed\") {\n\t\t\tlog.Printf(\"background color skipped: DC likely released\")\n\t\t\treturn\n\t\t}\n\t\tpanic(r)\n\t}\n}()\nw32.SetBkColor(hdc, color)","preventionTips":["Scope all background-color calls to the active paint block.","Guard against double release of the DC.","Draw synchronously in the paint handler rather than deferring to callbacks."],"tags":["windows","gdi","text-rendering","syscall","panic"],"backgroundTag":null,"analyzedSha":"0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3","analyzedAt":"2026-08-15T14:17:36.034Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}