cilium/cilium · error
updating link %s for program %s: %w
Error message
updating link %s for program %s: %w
What it means
This is the default branch of the UpdateLink switch in attachXDPProgram: any error that is neither nil, ENOENT (no link yet), nor ENOLINK (defunct) is reported as a failure to update the existing bpf_link pin. It means Cilium found an existing pinned link but could not swap in the new program via link update.
Source
Thrown at pkg/datapath/loader/xdp.go:338
case errors.Is(err, unix.ENOLINK):
if err := os.Remove(pin); err != nil {
return fmt.Errorf("unpinning defunct link %s: %w", pin, err)
}
logger.Info("Unpinned defunct link for program",
logfields.Link, pin,
logfields.ProgName, progName,
)
// No existing link found, continue trying to create one.
case errors.Is(err, os.ErrNotExist):
logger.Info("No existing link found for program",
logfields.Link, pin,
logfields.ProgName, progName,
)
default:
return fmt.Errorf("updating link %s for program %s: %w", pin, progName, err)
}
if err := bpf.MkdirBPF(bpffsDir); err != nil {
return fmt.Errorf("creating bpffs link dir for xdp attachment to device %s: %w", iface.Attrs().Name, err)
}
// Create a new link. This will only succeed on nodes that support bpf_link
// and don't have any XDP programs attached through netlink.
l, err := link.AttachXDP(link.XDPOptions{
Program: prog,
Interface: iface.Attrs().Index,
Flags: flags,
})
if err == nil {
defer func() {
// The program was successfully attached using bpf_link. Closing a link
// does not detach the program if the link is pinned.
if err := l.Close(); err != nil {View on GitHub (pinned to ac7b90affa)
Solutions
- Unpin the offending link (rm <pin> under /sys/fs/bpf/cilium/links/) and restart so a fresh link is created
- Grant required capabilities (CAP_BPF, CAP_NET_ADMIN, CAP_SYS_ADMIN) to the agent process
- Check wrapped error for EPERM/EINVAL and align program versions — wipe /var/run/cilium state after upgrades
- Verify the pin path contains an actual bpf_link (bpftool link show) not a leftover file
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure pin is a real link before update
if _, err := os.Stat(pin); err == nil {
if fi, _ := os.Stat(pin); fi.Mode().IsRegular() {
os.Remove(pin) // wrong type; let a fresh link be created
}
} Try / catch
// Go: on update failure, unpin and retry once
if err := bpf.UpdateLink(pin, prog); err != nil {
os.Remove(pin)
err = bpf.UpdateLink(pin, prog)
if err != nil { return fmt.Errorf("updating link %s for program %s: %w", pin, progName, err) }
} Prevention
- Wipe pinned links after Cilium upgrades to avoid type/flag mismatches with old links
- Grant CAP_BPF/CAP_SYS_ADMIN so link updates are permitted
- Avoid sharing bpffs directories between different agent versions
- Validate kernel supports bpf_link (kernel >= 5.7) before relying on pinned links
When it happens
Trigger: bpf.UpdateLink(pin, prog) returns EPERM (insufficient privileges), EINVAL (program type mismatch with existing link), EEXIST-class errors, or I/O errors on bpffs while updating an existing pinned link for progName.
Common situations: Older Cilium version pinned a link and the running process lacks capabilities to update it; pin file on bpffs corrupted or replaced by a non-link file; kernel version mismatch between the pinned link and the new program semantics.
Related errors
- attaching program %s using bpf_link: %w
- loading program: %w
- updating link %s for program %s: %w
- updating link %s for program %s: %w
- Unable to determine if BPF_FIB_LOOKUP_SKIP_NEIGH is supporte
AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31).
Data as JSON: /api/errors/0d51bc1f3ee700a7.
Report an issue: GitHub.