{"record":{"id":"ef92f1ff3e283217","repo":"BoundaryML/baml","slug":"failed-to-call-destructor-w","errorCode":null,"errorMessage":"failed to call destructor: %w","messagePattern":"failed to call destructor: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"engine/language_client_go/baml_go/raw_objects/utils.go","lineNumber":147,"sourceCode":"\n\tvar content_holder cffi.InvocationResponse\n\terr = proto.Unmarshal(content_bytes, &content_holder)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to unmarshal content bytes: %w\", err)\n\t}\n\tparsed, err := decodeObjectResponse(rt, &content_holder)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode object response: %w\", err)\n\t}\n\n\treturn parsed, nil\n}\n\nfunc destructor(object RawPointer) error {\n\tresult, err := CallMethod(object, \"~destructor\", nil)\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to call destructor: %w\", err)\n\t}\n\n\tif result != nil {\n\t\treturn fmt.Errorf(\"destructor returned unexpected result: %v\", result)\n\t}\n\treturn nil\n}\n\nfunc CallMethod(object RawPointer, method_name string, kwargs map[string]any) (any, error) {\n\tcffi_kwargs, err := serde.EncodeMapEntries(kwargs, \"function arguments\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"encoding method arguments: %w\", err)\n\t}\n\n\targs := cffi.BamlObjectMethodInvocation{\n\t\tKwargs:     cffi_kwargs,\n\t\tObject:     EncodeRawObject(object),\n\t\tMethodName: method_name,","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/language_client_go/baml_go/raw_objects/utils.go#L129-L165","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: runtime goes out of scope immediately\nfunc f() { rt := b.NewRuntime(); c := b.NewCollector(rt) }\n// after: keep runtime referenced for program lifetime\nvar rt b.Runtime\nfunc main() { rt = b.NewRuntime(); c := b.NewCollector(rt); defer func(){ _ = rt }() }","handlingStrategy":"fallback","validationCode":"// ensure the runtime is referenced for the lifetime of all BAML objects\nvar keepAlive b.Runtime = rt","typeGuard":null,"tryCatchPattern":"// not catchable at call sites: finalizer errors go to FFI logs\n// monitor logs instead:\n// grep CLIENT_GO_DESTRUCTOR_ERROR $BAML_FFI_CLIENT_LOG","preventionTips":["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"],"tags":["go","ffi","destructor","garbage-collection"],"backgroundTag":"internal-invariant-violation","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}