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
- Run delve with sudo or as root: 'sudo dlv trace --ebpf ...' or 'sudo go test -run TestTraceEBPF ./cmd/dlv'.
- 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.
- Raise the memlock limit: 'ulimit -l unlimited' in the shell, or set LimitMEMLOCK=infinity in the systemd unit.
- 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
- Run eBPF-backed sessions/tests with sudo or in a --privileged container, as the project's own test docs recommend.
- Set LimitMEMLOCK=infinity in systemd units and 'ulimit -l unlimited' in shells used for tracing.
- Grant CAP_SYS_RESOURCE (plus CAP_BPF/CAP_PERFMON on newer kernels) to the delve binary or container rather than full root where possible.
- Probe privilege/memlock limits at startup and fall back to breakpoint-based tracing when eBPF cannot load.
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
- no eBPF program loaded
- eBPF map not loaded
- too many arguments in traced function, max is 12 input+retur
- could not set options for new traced thread %d %s
- could not read auxiliary vector: %v
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/eacbc005af469072.
Report an issue: GitHub.