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

  1. Verify the file exists and is readable: ls -l /usr/lib/libSystem.B.dylib
  2. Run the binary outside any sandbox/seatbelt profile and retry
  3. Confirm the binary runs on real macOS (not Linux via cross-compiled mistake); this file only builds on darwin
  4. Check codesigning/hardened-runtime entitlements that may block library loading
  5. 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

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.