wailsapp/wails · error

CreateStreamOnHGlobal failed with E_INVALIDARG

Error message

CreateStreamOnHGlobal failed with E_INVALIDARG

What it means

The Go wrapper around the Win32 CreateStreamOnHGlobal API panicked because the call returned E_INVALIDARG. This API creates an IStream over a global-memory block (HGLOBAL); E_INVALIDARG means the arguments were rejected, almost always because hGlobal is 0, was not allocated by GlobalAlloc, or the out-pointer for the stream was invalid. The wrapper converts the HRESULT into a Go panic instead of returning an error.

Source

Thrown at v3/pkg/w32/ole32.go:83

	case E_OUTOFMEMORY:
		panic("CoCreateInstance failed with E_OUTOFMEMORY")
	case E_UNEXPECTED:
		panic("CoCreateInstance failed with E_UNEXPECTED")
	}

	return HRESULT(ret)
}

func CreateStreamOnHGlobal(hGlobal HGLOBAL, fDeleteOnRelease bool) *IStream {
	stream := new(IStream)
	ret, _, _ := procCreateStreamOnHGlobal.Call(
		uintptr(hGlobal),
		uintptr(BoolToBOOL(fDeleteOnRelease)),
		uintptr(unsafe.Pointer(&stream)))

	switch uint32(ret) {
	case E_INVALIDARG:
		panic("CreateStreamOnHGlobal failed with E_INVALIDARG")
	case E_OUTOFMEMORY:
		panic("CreateStreamOnHGlobal failed with E_OUTOFMEMORY")
	case E_UNEXPECTED:
		panic("CreateStreamOnHGlobal failed with E_UNEXPECTED")
	}

	return stream
}
func OleInitialise() {
	procOleInitialize.Call()
}

func RegisterDragDrop(hwnd HWND, dropTarget *DropTarget) error {

	dt := combridge.New[iDropTarget](dropTarget)
	hr, _, _ := procRegisterDragDrop.Call(
		hwnd,
		dt.Ref(),

View on GitHub (pinned to 0e754b1b40)

Solutions

  1. Check the HGLOBAL is non-zero immediately after GlobalAlloc and before calling CreateStreamOnHGlobal
  2. Ensure the buffer was allocated with GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE,...) — streams require a movable allocation, not GMEM_FIXED in some paths
  3. Do not GlobalFree the handle yourself when fDeleteOnRelease=true; let the stream own it
  4. Wrap the call in a recover() to turn the panic into an error if you cannot fully validate the handle

Example fix

// before
h := GlobalAlloc(GMEM_MOVEABLE, size)
stream := w32.CreateStreamOnHGlobal(h, true) // panics if h == 0

// after
h := GlobalAlloc(GMEM_MOVEABLE, size)
if h == 0 {
    return fmt.Errorf("GlobalAlloc failed: %w", errno.ERROR_NOT_ENOUGH_MEMORY)
}
stream := w32.CreateStreamOnHGlobal(h, true)
Defensive patterns

Strategy: validation

Validate before calling

if hGlobal == 0 {
    return fmt.Errorf("invalid HGLOBAL (0); GlobalAlloc probably failed")
}
stream := w32.CreateStreamOnHGlobal(hGlobal, true)

Try / catch

defer func() {
    if r := recover(); r != nil {
        return fmt.Errorf("CreateStreamOnHGlobal failed: %v", r)
    }
}()
stream := w32.CreateStreamOnHGlobal(h, true)

Prevention

When it happens

Trigger: Calling w32.CreateStreamOnHGlobal with a nil/zero HGLOBAL (e.g. a failed GlobalAlloc whose error was ignored), passing a handle from a different allocator (LocalAlloc/heap), or calling after the HGLOBAL was already freed with GlobalFree.

Common situations: Clipboard (SetClipboardData) and drag-and-drop (DROPDESCRIPTION / SHDoDragDrop) helper code that builds an IStream from an HGLOBAL without checking GlobalAlloc's return; freeing the HGLOBAL before the stream is released; double-create on the same handle with fDeleteOnRelease=true.

Related errors


AI-assisted analysis of wailsapp/wails@0e754b1b40 (2026-08-15). Data as JSON: /api/errors/a6ac9b261d1871f3. Report an issue: GitHub.