BoundaryML/baml · error
dlclose failed: %s
Error message
dlclose failed: %s
What it means
This error is returned by closeLibrary when the cgo dlclose() call fails to unload the BAML native shared library on Unix platforms. dlclose() returns nonzero when the handle is invalid or the library cannot be unloaded, and the library then surfaces the OS-provided dlerror() string for diagnosis. It almost always indicates a corrupted or invalid library handle rather than a problem with BAML configuration itself.
Source
Thrown at engine/language_client_go/baml_go/lib_unix.go:80
if errStr != "" {
errMsg += fmt.Sprintf(": %s", errStr)
} else {
errMsg += ": symbol not found"
}
return nil, fmt.Errorf("%s", errMsg)
}
return symbol, nil
}
// closeLibrary closes the loaded library
func closeLibrary(handle unsafe.Pointer) error {
if handle == nil {
return nil
}
if C.dlclose(handle) != 0 {
errStr := C.GoString(C.dlerror())
if errStr != "" {
return fmt.Errorf("dlclose failed: %s", errStr)
}
return fmt.Errorf("dlclose failed")
}
return nil
}
// platformInit performs any platform-specific initialization
func platformInit() error {
// Unix doesn't need special initialization
return nil
}View on GitHub (pinned to bd85ce9dee)
Solutions
- Ensure closeLibrary is called at most once per successful loadLibrary handle; guard cleanup with a sync.Once or nil-out the handle after closing.
- Read the %s detail from dlerror() to identify the underlying linker error and act on it (e.g. missing dependency of the .so).
- Verify the bundled BAML native library file is intact and matches your baml Go module version (reinstall/refresh the dependency).
- If the error appears only in tests, avoid unloading the library between test cases; load once per process.
Example fix
// before
if err := closeLibrary(handle); err != nil {
return err
}
handle = maybeStaleHandle
// after
var closeOnce sync.Once
closeOnce.Do(func() {
if err := closeLibrary(handle); err != nil {
log.Printf("dlclose warning: %v", err)
}
handle = nil
}) Defensive patterns
Strategy: try-catch
Validate before calling
if handle != nil && !closed {
_ = closeLibrary(handle)
closed = true
} Type guard
func canClose(h unsafe.Pointer, closed bool) bool {
return h != nil && !closed
} Try / catch
if err := closeLibrary(handle); err != nil {
log.Printf("dlclose warning (non-fatal at process exit): %v", err)
} Prevention
- Close the library handle exactly once per load, using sync.Once.
- Nil out the handle after closing so stale reuse is impossible.
- Treat unload failures at process exit as non-fatal and log them.
When it happens
Trigger: Calling code that obtained a handle via loadLibrary (dlopen) and then calls closeLibrary with a handle that was already closed, was invalidated, or was corrupted. Also occurs if the dynamic linker refuses to unload the .so due to lingering references or linker state.
Common situations: Double-closing the library in cleanup/teardown paths; tests that load and unload the native library multiple times in one process; corrupted native binaries or mismatched library versions shipped with the Go bindings; platforms with unusual dlopen/dlclose semantics (e.g. musl vs glibc).
Related errors
- dlclose failed
- FreeLibrary failed: %w
- failed to exec baml-cli: {err}
- failed to exec {}: {err}{origin}
- toolchain binary is not executable: {}{origin}
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/f3ca546b51543dad.
Report an issue: GitHub.