cilium/cilium · error

program %s is nil

Error message

program %s is nil

What it means

attachXDPProgram validates that the ebpf.Program handle for progName is non-nil before attaching. Cilium throws 'program %s is nil' when the collection/spec did not contain the expected named program, so attaching cannot proceed. This is a defensive check against loading a datapath object that lacks the required XDP program symbol.

Source

Thrown at pkg/datapath/loader/xdp.go:301

	}
	if err != nil {
		return fmt.Errorf("attaching XDP program: %w", err)
	}

	if err := commit(); err != nil {
		return fmt.Errorf("committing bpf pins: %w", err)
	}

	return nil
}

// attachXDPProgram attaches prog with the given progName to link.
//
// bpffsDir should exist and point to the links/ subdirectory in the per-device
// bpffs directory.
func attachXDPProgram(logger *slog.Logger, iface netlink.Link, prog *ebpf.Program, progName, bpffsDir string, flags link.XDPAttachFlags) error {
	if prog == nil {
		return fmt.Errorf("program %s is nil", progName)
	}

	// Attempt to open and update an existing link.
	pin := filepath.Join(bpffsDir, progName)
	err := bpf.UpdateLink(pin, prog)
	switch {
	// Update successful, nothing left to do.
	case err == nil:
		logger.Info("Updated link for program",
			logfields.Link, pin,
			logfields.ProgName, progName,
		)

		return nil

	// Link exists, but is defunct, and needs to be recreated. The program
	// no longer gets triggered at this point and the link needs to be removed
	// to proceed.

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Verify the compiled ELF contains progName (llvm-objdump -h / bpftool prog) and recompile the datapath
  2. Ensure Cilium image and node bpf sources are from the same version; wipe /var/run/cilium/state and restart
  3. Check the loader was handed the correct object path for XDP (not the generic bpf.o)
  4. If integrating programmatically, confirm the collection map key/program name matches the ELF symbol

Example fix

// before
prog := coll.Programs[progName]
err := attachXDPProgram(logger, iface, prog, progName, dir, flags)
// after
prog, ok := coll.Programs[progName]
if !ok || prog == nil {
    return fmt.Errorf("collection %s lacks program %s; recompile datapath", objPath, progName)
}
err := attachXDPProgram(logger, iface, prog, progName, dir, flags)
Defensive patterns

Strategy: type-guard

Validate before calling

// after loading the collection
prog, ok := coll.Programs[progName]
if !ok || prog == nil {
    return fmt.Errorf("program %s missing from %s", progName, objPath)
}

Type guard

func programExists(coll *ebpf.Collection, name string) (*ebpf.Program, bool) {
    if coll == nil { return nil, false }
    p, ok := coll.Programs[name]
    return p, ok && p != nil
}

Try / catch

// Go: guard before attach
if prog == nil {
    return fmt.Errorf("cannot attach: program %s not loaded; recompile datapath", progName)
}

Prevention

When it happens

Trigger: spec/collection lookup for progName (e.g. the XDP entrypoint program) yields nil because the compiled object's program name changed, the ELF was built from wrong source files, or the wrong object was compiled/loaded.

Common situations: Mistmatched bpf_xdp.c source and expected program name after a Cilium upgrade; build script compiling the wrong .o; datapath source-tree/bpf objects out of sync between cilium image and node state.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/b870cc537ecf1483. Report an issue: GitHub.