{"record":{"id":"f8c32abd0bc14ef4","repo":"cilium/cilium","slug":"opening-xdp-program-id-d-w","errorCode":null,"errorMessage":"opening XDP program id %d: %w","messagePattern":"opening XDP program id (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/datapath/loader/xdp.go","lineNumber":435,"sourceCode":"\terr = bpf.UnpinLink(pin)\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif !errors.Is(err, os.ErrNotExist) {\n\t\t// The pinned link exists, something went wrong unpinning it.\n\t\treturn fmt.Errorf(\"unpinning XDP program using bpf_link: %w\", err)\n\t}\n\n\txdp := iface.Attrs().Xdp\n\tif xdp == nil || !xdp.Attached {\n\t\treturn nil\n\t}\n\n\t// Inspect the attached program to only remove the intended XDP program.\n\tid := xdp.ProgId\n\tprog, err := ebpf.NewProgramFromID(ebpf.ProgramID(id))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"opening XDP program id %d: %w\", id, err)\n\t}\n\tinfo, err := prog.Info()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"getting XDP program info %d: %w\", id, err)\n\t}\n\t// The program name returned by BPF_PROG_INFO is limited to 20 characters.\n\t// Treat the kernel-provided program name as a prefix that needs to match\n\t// against progName. Empty program names (on kernels before 4.15) will always\n\t// match and be removed.\n\tif !strings.HasPrefix(progName, info.Name) {\n\t\treturn nil\n\t}\n\n\t// Pin doesn't exist, fall through to detaching using netlink.\n\tif err := netlink.LinkSetXdpFdWithFlags(iface, -1, int(link.XDPGenericMode)); err != nil {\n\t\treturn fmt.Errorf(\"detaching generic-mode XDP program using netlink: %w\", err)\n\t}\n","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/pkg/datapath/loader/xdp.go#L417-L453","documentation":"After the pin is missing, DetachXDP inspects the kernel-attached XDP program by its ID (xdp.ProgId) using ebpf.NewProgramFromID. The program with that ID could not be opened — typically it was already unloaded, or the caller lacks BPF privileges.","triggerScenarios":"DetachXDP on an interface whose netlink attrs report an attached XDP program with ProgId, but NewProgramFromID fails because the program no longer exists or fd limits/permissions block BPF_PROG_GET_FD_BY_ID.","commonSituations":"The XDP program was detached/destroyed by another process between reading attrs and opening it; agent container lacks CAP_BPF/CAP_SYS_ADMIN; running kernel doesn't support BPF_PROG_GET_FD_BY_ID (very old kernels).","solutions":["Treat it as already-detached if the program vanished (race) and retry the detach once","Grant the agent CAP_BPF and CAP_SYS_ADMIN (or run privileged) so BPF program handles can be opened","Verify with bpftool prog list that the program ID still exists","Upgrade cilium/cilium-ebpf and kernel if BPF_PROG_GET_FD_BY_ID is unsupported"],"exampleFix":"// before\nprog, err := ebpf.NewProgramFromID(ebpf.ProgramID(id))\nif err != nil { return fmt.Errorf(\"opening XDP program id %d: %w\", id, err) }\n// after\nprog, err := ebpf.NewProgramFromID(ebpf.ProgramID(id))\nif errors.Is(err, os.ErrNotExist) {\n    log.Debugf(\"XDP prog %d already gone; treating as detached\", id)\n    return nil\n}\nif err != nil { return fmt.Errorf(\"opening XDP program id %d: %w\", id, err) }","handlingStrategy":"fallback","validationCode":"ids, _ := ebpf.ProgramIDs(ebpf.XDP); contains(ids, progID) or skip","typeGuard":null,"tryCatchPattern":"prog, err := ebpf.NewProgramFromID(id)\nif errors.Is(err, os.ErrNotExist) { /* program already gone: treat as detached */ return nil }","preventionTips":["Grant CAP_BPF/CAP_SYS_ADMIN to the process","Check bpftool prog list before inspecting by ID","Treat ErrNotExist as success in cleanup paths"],"tags":["xdp","ebpf","bpf-privileges","race"],"backgroundTag":"bpf-program-open-failed","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}