BoundaryML/baml · warning
failed to call destructor: %w
Error message
failed to call destructor: %w
What it means
This error wraps any failure while invoking the '~destructor' object method over FFI during Go garbage collection. When a BAML raw object is finalized, destructor calls CallMethod(object, "~destructor", nil); any error from that call (marshalling, FFI call, unmarshalling) is wrapped here. It surfaces as a destructor error in FFI logs, not normally as an application-level panic.
Source
Thrown at engine/language_client_go/baml_go/raw_objects/utils.go:147
var content_holder cffi.InvocationResponse
err = proto.Unmarshal(content_bytes, &content_holder)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal content bytes: %w", err)
}
parsed, err := decodeObjectResponse(rt, &content_holder)
if err != nil {
return nil, fmt.Errorf("failed to decode object response: %w", err)
}
return parsed, nil
}
func destructor(object RawPointer) error {
result, err := CallMethod(object, "~destructor", nil)
if err != nil {
return fmt.Errorf("failed to call destructor: %w", err)
}
if result != nil {
return fmt.Errorf("destructor returned unexpected result: %v", result)
}
return nil
}
func CallMethod(object RawPointer, method_name string, kwargs map[string]any) (any, error) {
cffi_kwargs, err := serde.EncodeMapEntries(kwargs, "function arguments")
if err != nil {
return nil, fmt.Errorf("encoding method arguments: %w", err)
}
args := cffi.BamlObjectMethodInvocation{
Kwargs: cffi_kwargs,
Object: EncodeRawObject(object),
MethodName: method_name,View on GitHub (pinned to bd85ce9dee)
Solutions
- Keep the baml.Runtime alive (hold a reference) until all BAML objects are garbage collected
- Check FFI logs (BAML_FFI_CLIENT_LOG) for the underlying destructor error
- Avoid explicitly freeing/shutting down the runtime while objects may still be referenced
- Report persistent occurrences with logs; finalizer failures usually indicate a runtime lifetime bug
Example fix
// before: runtime goes out of scope immediately
func f() { rt := b.NewRuntime(); c := b.NewCollector(rt) }
// after: keep runtime referenced for program lifetime
var rt b.Runtime
func main() { rt = b.NewRuntime(); c := b.NewCollector(rt); defer func(){ _ = rt }() } Defensive patterns
Strategy: fallback
Validate before calling
// ensure the runtime is referenced for the lifetime of all BAML objects var keepAlive b.Runtime = rt
Try / catch
// not catchable at call sites: finalizer errors go to FFI logs // monitor logs instead: // grep CLIENT_GO_DESTRUCTOR_ERROR $BAML_FFI_CLIENT_LOG
Prevention
- Hold a package-level reference to baml.Runtime so it outlives all objects
- Avoid closing/freeing the runtime while objects are alive
- Enable BAML_FFI_CLIENT_LOG to detect destructor failures
When it happens
Trigger: Garbage collector finalizes a BamlObjectHandle-backed object (collector, function log, media, type builder, etc.) and the '~destructor' FFI call fails, e.g. because the runtime was already torn down.
Common situations: Application shutdown where the BAML runtime is freed before GC finalizers run; objects from a runtime that has been explicitly released; native library version mismatch.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- destructor returned unexpected result: %v
- panic(initErr)
- internal error: attempted to register unknown function '%s'
- failed to unmarshal content bytes: %w
- failed to decode object response: %w
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/ef92f1ff3e283217.
Report an issue: GitHub.