{"record":{"id":"373c942e9392a667","repo":"wailsapp/wails","slug":"cancellablepromise-does-not-support-transparent-su","errorCode":null,"errorMessage":"CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property.","messagePattern":"CancellablePromise does not support transparent subclassing\\. Please refrain from overriding the \\[Symbol\\.species\\] static property\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"v3/internal/runtime/desktop/@wailsio/runtime/src/cancellable.ts","lineNumber":192,"sourceCode":"     *                      It will be called _synchronously_ with a cancellation cause\n     *                      when cancellation is requested, _after_ the promise has already rejected\n     *                      with a {@link CancelError}, but _before_\n     *                      any {@link then}/{@link catch}/{@link finally} callback runs.\n     *                      If the callback returns a thenable, the promise returned from {@link cancel}\n     *                      will only fulfill after the former has settled.\n     *                      Unhandled exceptions or rejections from the callback will be wrapped\n     *                      in a {@link CancelledRejectionError} and bubbled up as unhandled rejections.\n     *                      If the `resolve` callback is called before cancellation with a cancellable promise,\n     *                      cancellation requests on this promise will be diverted to that promise,\n     *                      and the original `oncancelled` callback will be discarded.\n     */\n    constructor(executor: CancellablePromiseExecutor<T>, oncancelled?: CancellablePromiseCanceller) {\n        let resolve!: (value: T | PromiseLike<T>) => void;\n        let reject!: (reason?: any) => void;\n        super((res, rej) => { resolve = res; reject = rej; });\n\n        if ((this.constructor as any)[species] !== Promise) {\n            throw new TypeError(\"CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property.\");\n        }\n\n        let promise: CancellablePromiseWithResolvers<T> = {\n            promise: this,\n            resolve,\n            reject,\n            get oncancelled() { return oncancelled ?? null; },\n            set oncancelled(cb) { oncancelled = cb ?? undefined; }\n        };\n\n        const state: CancellablePromiseState = {\n            get root() { return state; },\n            resolving: false,\n            settled: false\n        };\n\n        // Setup cancellation system.\n        void Object.defineProperties(this, {","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/wailsapp/wails/blob/0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3/v3/internal/runtime/desktop/@wailsio/runtime/src/cancellable.ts#L174-L210","documentation":"SetBkMode sets how GDI draws text/background hatching — TRANSPARENT or OPAQUE — and returns the previous mode. The w32 wrapper panics with 'SetBkMode failed' when the API returns 0, which happens when the mode argument is neither TRANSPARENT(1) nor OPAQUE(2), or the HDC is invalid. Because 0 can also be a legitimate 'previous mode was 0' sentinel in edge cases, the wrapper's check is aggressive, but in practice the trigger is a bad argument or dead DC.","triggerScenarios":"Passing a mode constant defined in your own package with the wrong numeric value (e.g. 0 or 3+); calling SetBkMode with an HDC released by EndPaint/ReleaseDC; text-drawing helpers invoked during window destruction.","commonSituations":"Custom text rendering with TRANSPARENT background set before DrawText; the constant was hand-rolled instead of using the w32 constant so a typo'd value compiles fine but panics at runtime; ordering bugs where painting continues after WM_DESTROY.","solutions":["Use the package constants: pass w32.TRANSPARENT or w32.OPAQUE (1 and 2) rather than ad-hoc integers.","Verify the HDC lifetime: SetBkMode must run between BeginPaint/EndPaint or GetDC/ReleaseDC pairs in the same message handler.","Add a defensive check that the mode is 1 or 2 before calling when the value comes from config or user input.","If the panic occurs at shutdown, stop the paint pipeline when the window is closing."],"exampleFix":"// before\nw32.SetBkMode(hdc, bkMode) // bkMode == 0 from unset config -> panic\n\n// after\nif bkMode != w32.TRANSPARENT && bkMode != w32.OPAQUE {\n\tbkMode = w32.OPAQUE\n}\nw32.SetBkMode(hdc, bkMode)","handlingStrategy":"validation","validationCode":"func validBkMode(mode int) bool {\n\treturn mode == w32.TRANSPARENT || mode == w32.OPAQUE\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n\tif r := recover(); r != nil {\n\t\tif msg, _ := r.(string); strings.HasPrefix(msg, \"SetBkMode failed\") {\n\t\t\tlog.Printf(\"text background mode rejected, defaulting\")\n\t\t\treturn\n\t\t}\n\t\tpanic(r)\n\t}\n}()\nw32.SetBkMode(hdc, mode)","preventionTips":["Always use the package constants w32.TRANSPARENT / w32.OPAQUE, never raw integers.","Validate config-supplied mode values against the two legal constants before painting.","Keep HDC usage inside its owning paint scope."],"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"}