cilium/cilium · error

unable to replace the mtu %d for the route %s: %s

Error message

unable to replace the mtu %d for the route %s: %s

What it means

While iterating IPv4 routes in route-MTU mode, the plugin rewrites each route's MTU to Cilium's RouteMTU via netlink.RouteReplace. This error reports the target MTU, the route string, and the kernel's error message when the replacement fails. It means the pod's routing table could not be updated, which can break Cilium's egress/ingress datapath assumptions for the chained mode.

Source

Thrown at plugins/cilium-cni/chaining/generic-veth/generic-veth.go:150

			break
		}

		if !linkFound {
			return errors.New("no link found inside container")
		}

		if pluginCtx.NetConf.EnableRouteMTU || pluginCtx.CiliumConf.EnableRouteMTUForCNIChaining {
			routes, err := safenetlink.RouteList(nil, netlink.FAMILY_V4)
			if err != nil {
				err = fmt.Errorf("unable to list the IPv4 routes: %w", err)
				return err
			}
			for _, rt := range routes {
				if rt.MTU != int(pluginCtx.CiliumConf.RouteMTU) {
					rt.MTU = int(pluginCtx.CiliumConf.RouteMTU)
					err = netlink.RouteReplace(&rt)
					if err != nil {
						err = fmt.Errorf("unable to replace the mtu %d for the route %s: %s", rt.MTU, rt.String(), err.Error())
						return err
					}
				}
			}

			routes, err = safenetlink.RouteList(nil, netlink.FAMILY_V6)
			if err != nil {
				err = fmt.Errorf("unable to list the IPv6 routes: %w", err)
				return err
			}
			for _, rt := range routes {
				if rt.MTU != int(pluginCtx.CiliumConf.RouteMTU) {
					rt.MTU = int(pluginCtx.CiliumConf.RouteMTU)
					err = netlink.RouteReplace(&rt)
					if err != nil {
						err = fmt.Errorf("unable to replace the mtu %d for the route %s: %s", rt.MTU, rt.String(), err.Error())
						return err
					}

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Read the %s kernel error in the message: EPERM → restore CAP_NET_ADMIN; ESRCH/ENETUNREACH → the route vanished (retry the pod).
  2. Verify 'route-mtu' in the Cilium ConfigMap matches the underlying CNI's expectations (e.g. aws-cni's 9001).
  3. Ensure only one component manages route MTU: disable enableRouteMTU if the primary CNI already sets MTU.
  4. Retry pod creation; transient races usually resolve on retry.

Example fix

// before (Cilium ConfigMap)
enable-route-mtu: "true"
route-mtu: "0"
// after
enable-route-mtu: "true"
route-mtu: "9001"
Defensive patterns

Strategy: validation

Validate before calling

// Validate before enabling route MTU patching
if ciliumConf.RouteMTU < 68 || ciliumConf.RouteMTU > 65535 {
    return fmt.Errorf("invalid route-mtu %d configured", ciliumConf.RouteMTU)
}

Try / catch

err = netlink.RouteReplace(&rt)
if err != nil {
    switch {
    case errors.Is(err, os.ErrPermission):
        // missing CAP_NET_ADMIN in netns
    case errors.Is(err, syscall.ESRCH):
        // route removed concurrently — retry
    }
    return fmt.Errorf("unable to replace the mtu %d for the route %s: %s", rt.MTU, rt.String(), err.Error())
}

Prevention

When it happens

Trigger: netlink.RouteReplace(&rt) fails for a specific IPv4 route inside the container netns: e.g. EPERM (missing CAP_NET_ADMIN in the netns), the route was concurrently deleted, invalid MTU value, or the route is unreachable/locked by another manager.

Common situations: Container runtime dropping CAP_NET_ADMIN from the CNI plugin's netns context; conflicting MTU managers (both Cilium and a secondary CNI mutating MTU); RouteMTU set to 0 or an invalid value in the Cilium ConfigMap; route disappearing mid-loop due to concurrent networking changes.

Related errors


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