cilium/cilium · error

retrieving device %s: %w

Error message

retrieving device %s: %w

What it means

DeviceHasSKBProgramLoaded looks up the given network device to inspect whether a Cilium BPF program is attached; the safenetlink.LinkByName lookup itself failed, so attachment state could not be determined. This wraps the underlying netlink error (typically ENODEV).

Source

Thrown at pkg/datapath/loader/netlink.go:532

		return nil
	}

	if err := netlink.LinkSetName(link, to); err != nil {
		return fmt.Errorf("renaming device %s to %s: %w", from, to, err)
	}

	return nil
}

// DeviceHasSKBProgramLoaded returns true if the given device has a tc(x) program
// attached.
//
// If checkEgress is true, returns true if there's both an ingress and
// egress program attached.
func DeviceHasSKBProgramLoaded(device string, checkEgress bool) (bool, error) {
	link, err := safenetlink.LinkByName(device)
	if err != nil {
		return false, fmt.Errorf("retrieving device %s: %w", device, err)
	}

	itcx, err := hasCiliumTCXLinks(link, ebpf.AttachTCXIngress)
	if err != nil {
		return false, fmt.Errorf("failed to check for cilium tcx links on ingress: %w", err)
	}
	itc, err := hasCiliumTCFilters(link, netlink.HANDLE_MIN_INGRESS)
	if err != nil {
		return false, fmt.Errorf("failed to check for cilium tc filters on ingress: %w", err)
	}
	ink, err := hasCiliumNetkitLinks(link, ebpf.AttachNetkitPeer)
	if err != nil {
		return false, fmt.Errorf("failed to check for cilium netkit links: %w", err)
	}

	// Need ingress programs at minimum, bail out if these are already missing.
	if !itc && !itcx && !ink {
		return false, nil

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Verify the device exists: ip link show <device> in the same netns the agent runs in.
  2. Update the devices list in the CiliumConfig to match current interface names (device renames after reboot are common).
  3. If running from a test/tool, execute in the host network namespace (nsenter -t 1 -n) rather than a container netns.
  4. Re-run the check after the agent re-syncs its device list; transient ENODEV during pod teardown usually self-heals.

Example fix

// before
loaded, err := DeviceHasSKBProgramLoaded("eno1", true)
// after: guard the lookup
if _, err := net.InterfaceByName("eno1"); err != nil {
	return fmt.Errorf("device eno1 not present: %w", err)
}
loaded, err := DeviceHasSKBProgramLoaded("eno1", true)
Defensive patterns

Strategy: validation

Validate before calling

if _, err := net.InterfaceByName(device); err != nil {
	return fmt.Errorf("device %q not present in this netns: %w", device, err)
}
loaded, err := loader.DeviceHasSKBProgramLoaded(device, true)

Type guard

func deviceExists(name string) bool {
	_, err := net.InterfaceByName(name)
	return err == nil
}

Try / catch

loaded, err := loader.DeviceHasSKBProgramLoaded("eth0", true)
if err != nil {
	var eno unix.Errno
	if errors.As(err, &eno) && eno == unix.ENODEV {
		log.Printf("device missing; re-syncing Cilium device config")
	}
	return err
}

Prevention

When it happens

Trigger: Calling DeviceHasSKBProgramLoaded with a device name that does not exist in the current network namespace, a misspelled interface name, or when the device was removed after configuration was generated (device list out of sync).

Common situations: CiliumConfig devices changed (e.g. enp5s0 renamed to eth0 after reboot or by predictable-naming), datapath reload running in the wrong netns (e.g. inside a container instead of host), or veth devices torn down by pod deletion.

Related errors


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