BoundaryML/baml · error

library handle is nil when looking up symbol '%s'

Error message

library handle is nil when looking up symbol '%s'

What it means

getSymbol guards against looking up a symbol (function) in a library whose dlopen handle is nil. This is an internal invariant check: it means loadLibrary failed but a nil handle was still passed to symbol lookup, or handle propagation lost the error.

Source

Thrown at engine/language_client_go/baml_go/lib_unix.go:50

			dlopenErr = fmt.Errorf("%w (architecture mismatch)", dlopenErr)
		} else if strings.Contains(dlErrStr, "cannot open shared object file") {
			if strings.Contains(dlErrStr, "Permission denied") {
				dlopenErr = fmt.Errorf("%w (permission denied)", dlopenErr)
			} else {
				dlopenErr = fmt.Errorf("%w (file not found or inaccessible)", dlopenErr)
			}
		} else if strings.Contains(dlErrStr, "image not found") || strings.Contains(dlErrStr, "no such file or directory") {
			dlopenErr = fmt.Errorf("%w (library or dependency not found)", dlopenErr)
		}
		return nil, dlopenErr
	}
	return handle, nil
}

// getSymbol retrieves a symbol from the loaded library
func getSymbol(handle unsafe.Pointer, name string) (unsafe.Pointer, error) {
	if handle == nil {
		return nil, fmt.Errorf("library handle is nil when looking up symbol '%s'", name)
	}

	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))

	C.dlerror() // Clear any existing error
	symbol := C.dlsym(handle, cName)
	errStr := C.GoString(C.dlerror())

	if symbol == nil {
		errMsg := fmt.Sprintf("dlsym error for %s", name)
		if errStr != "" {
			errMsg += fmt.Sprintf(": %s", errStr)
		} else {
			errMsg += ": symbol not found"
		}
		return nil, fmt.Errorf("%s", errMsg)
	}

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Check and handle the error returned by loadLibrary before calling getSymbol
  2. Verify the library actually loaded (non-nil handle) before any symbol resolution
  3. Fix initialization flow so that a failed load short-circuits downstream symbol lookups
  4. Log the original dlopen error to identify why the handle was nil

Example fix

// before
handle, _ := loadLibrary(path)
sym, err := getSymbol(handle, "baml_init") // handle is nil
// after
handle, err := loadLibrary(path)
if err != nil {
    return fmt.Errorf("library load failed: %w", err)
}
sym, err := getSymbol(handle, "baml_init")
Defensive patterns

Strategy: type-guard

Validate before calling

handle, err := loadLibrary(path)
if err != nil || handle == nil {
    return fmt.Errorf("library unavailable: %w", err)
}

Type guard

func handleValid(h unsafe.Pointer) bool { return h != nil }

Try / catch

sym, err := getSymbol(handle, name)
if err != nil && strings.Contains(err.Error(), "library handle is nil") {
    // reload the library before retrying symbol lookup
    handle, rerr := loadLibrary(path)
    if rerr != nil { return rerr }
    sym, err = getSymbol(handle, name)
}

Prevention

When it happens

Trigger: Calling getSymbol(handle, name) when handle == nil — i.e., proceeding with symbol resolution after a failed dlopen or with an uninitialized handle.

Common situations: Caller code ignoring the loadLibrary error and continuing to resolve symbols; initialization ordering bugs where the library failed to load at startup but symbol lookups still run.

Related errors


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