go-delve/delve · error

failed to remove memlock limit (try running with CAP_SYS_RES

Error message

failed to remove memlock limit (try running with CAP_SYS_RESOURCE or as root): %w

What it means

LoadEBPFTracingProgram wraps the error returned by cilium/ebpf's rlimit.RemoveMemlock(), which raises the process RLIMIT_MEMLOCK limit so the eBPF program and its maps can be loaded into the kernel. Without this the kernel would refuse to allocate pinned/unpinned BPF map memory. Delve re-raises it with a hint that the process needs CAP_SYS_RESOURCE or root, because only privileged processes may raise the memlock limit on most kernels.

Source

Thrown at pkg/proc/internal/ebpf/helpers.go:247

	if len(ctx.parsedBpfEvents) == 0 {
		return make([]RawUProbeParams, 0)
	}

	events := make([]RawUProbeParams, len(ctx.parsedBpfEvents))
	copy(events, ctx.parsedBpfEvents)
	ctx.parsedBpfEvents = ctx.parsedBpfEvents[:0]
	return events
}

func LoadEBPFTracingProgram(path string) (*EBPFContext, error) {
	var (
		ctx  EBPFContext
		err  error
		objs traceObjects
	)

	if err = rlimit.RemoveMemlock(); err != nil {
		return nil, fmt.Errorf("failed to remove memlock limit (try running with CAP_SYS_RESOURCE or as root): %w", err)
	}
	ctx.executable, err = link.OpenExecutable(path)
	if err != nil {
		return nil, err
	}

	if err := loadTraceObjects(&objs, nil); err != nil {
		return nil, err
	}
	ctx.objs = &objs

	ctx.bpfRingBuf, err = ringbuf.NewReader(objs.Events)
	if err != nil {
		return nil, err
	}

	ctx.bpfArgMap = objs.ArgMap
	ctx.paramInfo = make(map[dwarfTypeKey]paramMeta)

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Run delve with sudo or as root: 'sudo dlv trace --ebpf ...' or 'sudo go test -run TestTraceEBPF ./cmd/dlv'.
  2. Grant the capability without full root: 'sudo setcap cap_sys_resource+ep <dlv-binary>' or add CAP_SYS_RESOURCE/CAP_BPF/CAP_PERFMON to the container.
  3. Raise the memlock limit: 'ulimit -l unlimited' in the shell, or set LimitMEMLOCK=infinity in the systemd unit.
  4. In Docker, run with --privileged (as the project's eBPF tests do) instead of a restricted container.

Example fix

// before
go test -run TestTraceEBPF3 -count 1 ./cmd/dlv
// error: failed to remove memlock limit ...
// after
sudo go test -run TestTraceEBPF3 -count 1 ./cmd/dlv
Defensive patterns

Strategy: try-catch

Validate before calling

// Before starting an eBPF trace session, check privilege/limits:
func canLoadEBPF() error {
	if os.Geteuid() == 0 {
		return nil
	}
	var lim unix.Rlimit
	if err := unix.Getrlimit(unix.RLIMIT_MEMLOCK, &lim); err != nil {
		return err
	}
	if lim.Cur < 8<<20 { // eBPF maps typically need more than a small memlock
		return fmt.Errorf("memlock limit %d too low; run as root or grant CAP_SYS_RESOURCE", lim.Cur)
	}
	return nil
}

Try / catch

ctx, err := ebpf.LoadEBPFTracingProgram(path)
if err != nil {
	if strings.Contains(err.Error(), "failed to remove memlock limit") {
		return fmt.Errorf("eBPF tracing requires elevated privileges: re-run with sudo or grant CAP_SYS_RESOURCE: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling LoadEBPFTracingProgram (invoked when starting a tracee session with --ebpf) as an unprivileged user whose RLIMIT_MEMLOCK cannot be raised: no root, no CAP_SYS_RESOURCE, and a hard memlock ulimit that is too low (e.g. 'ulimit -l' small or systemd LimitMEMLOCK not raised).

Common situations: Running 'dlv trace --ebpf' as a normal user; running inside a container without --privileged or with dropped capabilities; CI runners where the test user lacks CAP_SYS_RESOURCE; systemd units without LimitMEMLOCK=infinity.

Related errors


AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31). Data as JSON: /api/errors/eacbc005af469072. Report an issue: GitHub.