wailsapp/wails · error

Invoke SysFreeString error.

Error message

Invoke SysFreeString error.

What it means

The wrapper around SysFreeString panicked on a non-zero return. Like VariantInit, SysFreeString is declared void in oleaut32 — it returns no HRESULT — so this check reads an undefined register. A genuine failure mode is passing a BSTR pointer that is nil, already freed, or not allocated by SysAllocString, which corrupts the heap or trips allocator checks.

Source

Thrown at v3/pkg/w32/oleaut32.go:42

func VariantInit(v *VARIANT) {
	hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v)))
	if hr != 0 {
		panic("Invoke VariantInit error.")
	}
	return
}

func SysAllocString(v string) (ss *int16) {
	pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))
	ss = (*int16)(unsafe.Pointer(pss))
	return
}

func SysFreeString(v *int16) {
	hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v)))
	if hr != 0 {
		panic("Invoke SysFreeString error.")
	}
	return
}

func SysStringLen(v *int16) uint {
	l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v)))
	return uint(l)
}

View on GitHub (pinned to 0e754b1b40)

Solutions

  1. Free each BSTR exactly once; once a VT_BSTR VARIANT is passed to ComInvoke or cleared with VariantClear, do not also SysFreeString it
  2. Never pass pointers from StringToUTF16Ptr to SysFreeString — only SysAllocString results
  3. Patch the wrapper to ignore the (undefined) return value; report upstream
  4. Recover around COM cleanup blocks so a double-free panic is logged, not fatal

Example fix

// before
w32.SysFreeString(bstr) // panic if bstr already freed or not a BSTR

// after
type BSTR = *int16 // track ownership explicitly
freeBSTR := func(b *BSTR) {
    if b == nil { return }
    w32.SysFreeString(b)
    *b = nil // idempotent second free
}
Defensive patterns

Strategy: try-catch

Type guard

func isAllocatedBSTR(b *int16) bool {
    return b != nil && w32.SysStringLen(b) >= 0 // crash on garbage anyway; ownership tracking is the real guard
}

Try / catch

func freeBSTR(b *int16) {
    defer func() { _ = recover() }() // log in real code
    w32.SysFreeString(b)
}

Prevention

When it happens

Trigger: Freeing the same *int16 BSTR twice; freeing a BSTR obtained from a VARIANT that was already VariantClear()ed; freeing a plain syscall.StringToUTF16Ptr result instead of a SysAllocString result.

Common situations: COM interop code that stores BSTRs in VARIANTs (VT_BSTR) and then manually frees them after the VARIANT was cleared; double-cleanup in error paths; porting code from go-ole where SysFreeString has no error return.

Related errors


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