{"record":{"id":"f1d52e4d4be18095","repo":"wailsapp/wails","slug":"invoke-sysfreestring-error","errorCode":null,"errorMessage":"Invoke SysFreeString error.","messagePattern":"Invoke SysFreeString error\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/pkg/w32/oleaut32.go","lineNumber":42,"sourceCode":"\nfunc VariantInit(v *VARIANT) {\n\thr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v)))\n\tif hr != 0 {\n\t\tpanic(\"Invoke VariantInit error.\")\n\t}\n\treturn\n}\n\nfunc SysAllocString(v string) (ss *int16) {\n\tpss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))\n\tss = (*int16)(unsafe.Pointer(pss))\n\treturn\n}\n\nfunc SysFreeString(v *int16) {\n\thr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v)))\n\tif hr != 0 {\n\t\tpanic(\"Invoke SysFreeString error.\")\n\t}\n\treturn\n}\n\nfunc SysStringLen(v *int16) uint {\n\tl, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v)))\n\treturn uint(l)\n}\n","sourceCodeStart":24,"sourceCodeEnd":51,"githubUrl":"https://github.com/wailsapp/wails/blob/0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3/v3/pkg/w32/oleaut32.go#L24-L51","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Free each BSTR exactly once; once a VT_BSTR VARIANT is passed to ComInvoke or cleared with VariantClear, do not also SysFreeString it","Never pass pointers from StringToUTF16Ptr to SysFreeString — only SysAllocString results","Patch the wrapper to ignore the (undefined) return value; report upstream","Recover around COM cleanup blocks so a double-free panic is logged, not fatal"],"exampleFix":"// before\nw32.SysFreeString(bstr) // panic if bstr already freed or not a BSTR\n\n// after\ntype BSTR = *int16 // track ownership explicitly\nfreeBSTR := func(b *BSTR) {\n    if b == nil { return }\n    w32.SysFreeString(b)\n    *b = nil // idempotent second free\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isAllocatedBSTR(b *int16) bool {\n    return b != nil && w32.SysStringLen(b) >= 0 // crash on garbage anyway; ownership tracking is the real guard\n}","tryCatchPattern":"func freeBSTR(b *int16) {\n    defer func() { _ = recover() }() // log in real code\n    w32.SysFreeString(b)\n}","preventionTips":["Track BSTR ownership explicitly; free exactly once","After VariantClear or ComInvoke, treat the VARIANT's BSTR as gone","Only free pointers returned by SysAllocString"],"tags":["windows","com","bstr","memory","panic","wrapper-bug"],"backgroundTag":null,"analyzedSha":"0e754b1b40ba9044c2a1460e23b7c3de20fc5cf3","analyzedAt":"2026-08-15T14:17:36.034Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}