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

  1. Keep the baml.Runtime alive (hold a reference) until all BAML objects are garbage collected
  2. Check FFI logs (BAML_FFI_CLIENT_LOG) for the underlying destructor error
  3. Avoid explicitly freeing/shutting down the runtime while objects may still be referenced
  4. 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

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


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/ef92f1ff3e283217. Report an issue: GitHub.