{"record":{"id":"fb42f4017bf491a8","repo":"wailsapp/wails","slug":"invalidstateerror","errorCode":"InvalidStateError","errorMessage":"Still in CONNECTING state.","messagePattern":"Still in CONNECTING state\\.","errorType":"validation","errorClass":"DOMException","httpStatus":null,"severity":"error","filePath":"v3/internal/runtime/desktop/@wailsio/runtime/src/stream.ts","lineNumber":277,"sourceCode":"        // Fire and forget: the ack arrives as an open frame on the poll, which\n        // is what moves readyState to OPEN.\n        this._chain = this._chain.then(() =>\n            postFrame(this._id, KIND_OPEN, new Uint8Array(0), name, this._openAbort.signal)\n        ).catch((err) => {\n            this._fail(err);\n        });\n\n        startPolling();\n    }\n\n    /**\n     * Send a frame to Go. Accepts anything a WebSocket does; strings are\n     * encoded as UTF-8, since Go receives every frame as a byte slice.\n     */\n    send(data: string | ArrayBufferLike | ArrayBufferView | Blob): void {\n        if (this.readyState === WailsSocket.CONNECTING) {\n            // Matches the WebSocket specification.\n            throw new DOMException(\"Still in CONNECTING state.\", \"InvalidStateError\");\n        }\n        if (this.readyState !== WailsSocket.OPEN) {\n            return;\n        }\n\n        // Resolve now rather than inside the chain. Mutable binary inputs are\n        // copied synchronously so send() snapshots their bytes like a native\n        // WebSocket, even when a batch waits behind an in-flight request. A\n        // Blob is immutable and is therefore safe to read asynchronously once.\n        const immediate = toBytesSync(data);\n        const snapshot = immediate\n            ? Promise.resolve(immediate)\n            : toBytes(data, this._sendAbort.signal);\n        // close() may discard this promise before the flush has dequeued it.\n        // Attach a rejection observer immediately so cancelling an asynchronous\n        // Blob read cannot become an unhandled rejection; Promise.all still\n        // observes the original rejection when the flush already owns it.\n        void snapshot.catch(() => {});","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/wailsapp/wails/blob/0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3/v3/internal/runtime/desktop/@wailsio/runtime/src/stream.ts#L259-L295","documentation":"GlobalFree releases an HGLOBAL block; it returns NULL on success and the handle itself on failure. The w32 wrapper panics with 'GlobalFree failed' when the return is non-zero. The documented failure causes are an invalid handle (already freed or never allocated) or a locked object: memory allocated GMEM_MOVEABLE cannot be freed while its lock count from GlobalLock is non-zero — each GlobalLock must be followed by GlobalUnlock before freeing.","triggerScenarios":"Calling GlobalFree on a block that is still locked (GlobalUnlock not called or returned FALSE because of a prior unlock imbalance); double-freeing the same HGLOBAL; freeing a handle that ownership was transferred away from (e.g. already passed to SetClipboardData, which makes the system the owner).","commonSituations":"Clipboard code that GlobalLocks a buffer, copies data, SetClipboardData's it, then also GlobalFrees it — after hand-off to the clipboard that is a double-managed handle; error paths that skip GlobalUnlock but still reach GlobalFree.","solutions":["Ensure every GlobalLock has a matching GlobalUnlock before GlobalFree; call GlobalUnlock and let its FALSE return tell you the lock count hit zero.","Never GlobalFree an HGLOBAL after passing it to SetClipboardData — ownership transfers to the system.","Set the handle variable to zero after freeing to make double-free bugs obvious in review.","Verify the handle came from GlobalAlloc (or returned by an API documented to produce freeable HGLOBALs) and not another allocator."],"exampleFix":"// before\np := w32.GlobalLock(h)\ncopy(dst, sliceFrom(p))\nw32.GlobalFree(h) // still locked -> panic\n\n// after\np := w32.GlobalLock(h)\ncopy(dst, sliceFrom(p))\nw32.GlobalUnlock(h)\nw32.GlobalFree(h)","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call GlobalUnlock until it returns FALSE (lock count zero) before GlobalFree.","Never free an HGLOBAL already handed to SetClipboardData — the system owns it.","Zero out handle variables after free to expose double-frees in testing."],"tags":["windows","memory","clipboard","double-free","panic"],"backgroundTag":null,"analyzedSha":"0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3","analyzedAt":"2026-08-15T14:17:36.034Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}