wavetermdev/waveterm · error
dlopen %s: %w
Error message
dlopen %s: %w
What it means
This error wraps any failure from purego.Dlopen when lazily loading /usr/lib/libSystem.B.dylib on macOS during first use of procinfo.GetProcInfo. libSystem provides proc_pidinfo and mach_timebase_info, so without it the darwin process-info backend cannot work. The error is cached via sync.Once, so every subsequent call fails identically.
Source
Thrown at pkg/util/procinfo/procinfo_darwin.go:110
info.VmRSS = -1
info.NumThreads = -1
if ti, terr := getDarwinProcTaskInfo(pid); terr == nil {
if darwinTimeScale > 0 {
info.CpuUser = float64(ti.TotalUser) * darwinTimeScale / 1e9
info.CpuSys = float64(ti.TotalSystem) * darwinTimeScale / 1e9
}
info.VmRSS = int64(ti.ResidentSize)
info.NumThreads = ti.Threadnum
}
return info, nil
}
func initDarwinProcFuncs() error {
darwinProcOnce.Do(func() {
handle, err := purego.Dlopen(systemLibPath, purego.RTLD_LAZY|purego.RTLD_GLOBAL)
if err != nil {
darwinProcInitErr = fmt.Errorf("dlopen %s: %w", systemLibPath, err)
return
}
darwinLibHandle = handle
purego.RegisterLibFunc(&darwinProcPidInfo, darwinLibHandle, procPidInfoSym)
purego.RegisterLibFunc(&darwinMachTimebase, darwinLibHandle, machTimebaseSym)
var tb machTimebaseInfo
if rc := darwinMachTimebase(uintptr(unsafe.Pointer(&tb))); rc != kernSuccess {
darwinProcInitErr = fmt.Errorf("mach_timebase_info failed: %d", rc)
return
}
if tb.Denom == 0 {
darwinProcInitErr = fmt.Errorf("mach_timebase_info returned denom=0")
return
}
darwinTimeScale = float64(tb.Numer) / float64(tb.Denom)View on GitHub (pinned to a4447c1563)
Solutions
- Verify the file exists and is readable: ls -l /usr/lib/libSystem.B.dylib
- Run the binary outside any sandbox/seatbelt profile and retry
- Confirm the binary runs on real macOS (not Linux via cross-compiled mistake); this file only builds on darwin
- Check codesigning/hardened-runtime entitlements that may block library loading
- Update github.com/ebitengine/purego to the latest version
Example fix
// before
handle, err := purego.Dlopen(systemLibPath, purego.RTLD_LAZY|purego.RTLD_GLOBAL)
// after
if _, statErr := os.Stat(systemLibPath); statErr != nil {
return fmt.Errorf("libSystem missing at %s: %w", systemLibPath, statErr)
}
handle, err := purego.Dlopen(systemLibPath, purego.RTLD_LAZY|purego.RTLD_GLOBAL) Defensive patterns
Strategy: try-catch
Validate before calling
// Go: no direct pre-check, but verify the dylib exists before relying on procinfo
if _, err := os.Stat("/usr/lib/libSystem.B.dylib"); err != nil {
// darwin procinfo will fail; use an alternative source
} Try / catch
info, err := procinfo.GetProcInfo(ctx, nil, pid)
if err != nil {
if strings.Contains(err.Error(), "dlopen") {
// permanent init failure (cached by sync.Once): stop retrying, use fallback
}
return err
} Prevention
- Run on stock macOS without sandbox profiles that block /usr/lib
- Test procinfo once at startup and degrade gracefully if init fails
- Keep purego dependency up to date
- Remember sync.Once caches the failure — restart the process after fixing the environment
When it happens
Trigger: GetProcInfo (darwin) triggers initDarwinProcFuncs; Dlopen fails if the dylib is missing, unreadable, or the sandbox/seccomp profile blocks dlopen of /usr/lib/libSystem.B.dylib.
Common situations: Running inside a hardened/seatbelt sandbox or containerized macOS CI runner where /usr/lib is restricted; a stripped or nonstandard macOS system layout; code paths accidentally compiled/linked oddly so purego cannot resolve the loader.
Related errors
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/b273542628ee7d78.
Report an issue: GitHub.