wavetermdev/waveterm · error

mach_timebase_info returned denom=0

Error message

mach_timebase_info returned denom=0

What it means

mach_timebase_info succeeded but returned denom=0, which would cause a division by zero when computing darwinTimeScale = numer/denom. The library treats this defensively as an init failure and caches it via sync.Once, disabling CPU-time scaling for the process.

Source

Thrown at pkg/util/procinfo/procinfo_darwin.go:124

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)
	})
	return darwinProcInitErr
}

func getDarwinProcTaskInfo(pid int32) (*procTaskInfo, error) {
	if err := initDarwinProcFuncs(); err != nil {
		return nil, err
	}

	var ti procTaskInfo
	ret := darwinProcPidInfo(
		pid,
		procPidTaskInfo,
		0,

View on GitHub (pinned to a4447c1563)

Solutions

  1. Verify machTimebaseInfo matches the C struct mach_timebase_info { uint32 numer; uint32 denom; } (two uint32, no padding)
  2. Update purego and re-test; binding regressions can mis-map arguments
  3. Log the full tb struct (numer and denom) to confirm the corruption pattern
  4. Avoid re-running in the same process — sync.Once caches the error; restart after fixing
  5. Fall back to a known-good scale (numer=1, denom=1) if exact CPU times are non-critical

Example fix

// before
darwinTimeScale = float64(tb.Numer) / float64(tb.Denom)
// after
if tb.Denom == 0 {
    darwinProcInitErr = fmt.Errorf("mach_timebase_info returned denom=0 (numer=%d)", tb.Numer)
    return
}
darwinTimeScale = float64(tb.Numer) / float64(tb.Denom)
Defensive patterns

Strategy: try-catch

Try / catch

info, err := procinfo.GetProcInfo(ctx, nil, pid)
if err != nil && strings.Contains(err.Error(), "denom=0") {
    // timebase invalid; treat CPU-time data as unavailable
}

Prevention

When it happens

Trigger: First GetProcInfo call on darwin when the kernel fills machTimebaseInfo with Numer>0, Denom==0 — practically only from memory corruption, a misdeclared struct layout, or garbage from a bad FFI binding.

Common situations: Struct ABI mismatch after an OS/SDK change so the Denom field reads wrong memory; custom toolchains or emulators (e.g. Rosetta edge cases) returning malformed output.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/34125facb0886934. Report an issue: GitHub.