{"record":{"id":"58eb766b1f16aee8","repo":"wailsapp/wails","slug":"createstreamonhglobal-failed-with-e-unexpected","errorCode":null,"errorMessage":"CreateStreamOnHGlobal failed with E_UNEXPECTED","messagePattern":"CreateStreamOnHGlobal failed with E_UNEXPECTED","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/pkg/w32/ole32.go","lineNumber":87,"sourceCode":"\t}\n\n\treturn HRESULT(ret)\n}\n\nfunc CreateStreamOnHGlobal(hGlobal HGLOBAL, fDeleteOnRelease bool) *IStream {\n\tstream := new(IStream)\n\tret, _, _ := procCreateStreamOnHGlobal.Call(\n\t\tuintptr(hGlobal),\n\t\tuintptr(BoolToBOOL(fDeleteOnRelease)),\n\t\tuintptr(unsafe.Pointer(&stream)))\n\n\tswitch uint32(ret) {\n\tcase E_INVALIDARG:\n\t\tpanic(\"CreateStreamOnHGlobal failed with E_INVALIDARG\")\n\tcase E_OUTOFMEMORY:\n\t\tpanic(\"CreateStreamOnHGlobal failed with E_OUTOFMEMORY\")\n\tcase E_UNEXPECTED:\n\t\tpanic(\"CreateStreamOnHGlobal failed with E_UNEXPECTED\")\n\t}\n\n\treturn stream\n}\nfunc OleInitialise() {\n\tprocOleInitialize.Call()\n}\n\nfunc RegisterDragDrop(hwnd HWND, dropTarget *DropTarget) error {\n\n\tdt := combridge.New[iDropTarget](dropTarget)\n\thr, _, _ := procRegisterDragDrop.Call(\n\t\thwnd,\n\t\tdt.Ref(),\n\t)\n\n\tif hr != S_OK {\n\t\treturn syscall.Errno(hr)","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/wailsapp/wails/blob/0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3/v3/pkg/w32/ole32.go#L69-L105","documentation":"CreateStreamOnHGlobal returned E_UNEXPECTED. For this API it typically indicates the underlying HGLOBAL is in a bad state (already freed, locked with the wrong flags, or the handle reused after the previous stream deleted it) or that COM is unavailable on this thread. The wrapper panics because it treats all three documented failure codes as fatal.","triggerScenarios":"Calling CreateStreamOnHGlobal twice on the same HGLOBAL when the first stream was created with fDeleteOnRelease=true (first Release frees the handle); using a stale/dangling HGLOBAL; calling on a thread where OleInitialize has not been run.","commonSituations":"Retrying a clipboard write after a failure without reallocating the HGLOBAL; caching the HGLOBAL across operations while the stream deletes it; multi-threaded code touching OLE storage from a non-OLE-initialized goroutine.","solutions":["Allocate a fresh HGLOBAL for every CreateStreamOnHGlobal call; never reuse the handle after a stream with fDeleteOnRelease=true has been released","Call w32.OleInitialise() on the thread (usually the main/UI thread) before OLE operations","Audit the code path for a previous Release() that already consumed the memory","Wrap in recover() and retry the whole operation with a new allocation"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if hGlobal == 0 || !validOwnedHandle(hGlobal) {\n    return errors.New(\"stale HGLOBAL\")\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        err = fmt.Errorf(\"CreateStreamOnHGlobal: %v (stale handle or OLE not initialized?)\", r)\n    }\n}()","preventionTips":["Allocate a new HGLOBAL per stream; never share across streams","Call OleInitialise once on the UI thread before OLE work","After Release on a stream with fDeleteOnRelease, null the handle variable"],"tags":["windows","win32","ole","lifetime","panic"],"backgroundTag":null,"analyzedSha":"0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3","analyzedAt":"2026-08-15T14:17:36.034Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}