cilium/cilium · error
unable to mark orphaned CIDR %s still used by node %s as all
Error message
unable to mark orphaned CIDR %s still used by node %s as allocated: %w
What it means
During reconcileOrphanCIDRs, unorphanCIDR reclaims a CIDR that a node still uses but which the allocator lost track of (orphaned). It calls p.occupyCIDR to mark the orphaned CIDR as allocated again; if that fails (e.g. the CIDR is now allocated to a different node, or it is outside the pool's ranges), the error is wrapped with this message naming the CIDR and node.
Source
Thrown at operator/pkg/ipam/allocator/multipool/pool_allocator.go:188
case len(p.orphans[node][pool].v6) == 0:
cidrs := p.orphans[node][pool]
p.orphans[node][pool] = cidrSets{
v4: cidrs.v4,
allowFirstIP: cidrs.allowFirstIP,
allowLastIP: cidrs.allowLastIP,
}
}
}
func (p *PoolAllocator) unorphanCIDR(isV6 bool, node, pool string, cidr netip.Prefix) error {
p.logger.Info(
"CIDR from pool already in use by node, marking it as allocated",
logfields.CIDR, cidr,
logfields.PoolName, pool,
logfields.Node, node,
)
if err := p.occupyCIDR(node, pool, cidr); err != nil {
return fmt.Errorf("unable to mark orphaned CIDR %s still used by node %s as allocated: %w", cidr, node, err)
}
if isV6 {
delete(p.orphans[node][pool].v6, cidr)
} else {
delete(p.orphans[node][pool].v4, cidr)
}
return nil
}
func (p *PoolAllocator) reconcileOrphanCIDRs(pool string, v4, v6 []cidralloc.CIDRAllocator) error {
var errs []error
for node, cidrs := range p.orphans {
for pool, cidrSets := range cidrs {
for cidr := range cidrSets.v4 {
if containsCIDR(v4, cidr) {
errs = append(errs, p.unorphanCIDR(false, node, pool, cidr))
}
}View on GitHub (pinned to ac7b90affa)
Solutions
- Check which node now owns the CIDR (node specs / pool allocations) — if reassigned, clear the orphan record for the old node instead of forcing occupy.
- Confirm the pool's CIDR config still contains the orphaned prefix; restore it if it was changed mid-flight.
- Ensure single-operator operation (leader election) to prevent a second allocator from claiming orphaned CIDRs concurrently.
- Retry reconciliation: once the conflicting allocation is resolved, the next reconcileOrphanCIDRs pass can mark the CIDR allocated.
Example fix
// before
if err := p.occupyCIDR(node, pool, cidr); err != nil {
return fmt.Errorf("unable to mark orphaned CIDR %s still used by node %s as allocated: %w", cidr, node, err)
}
// after: tolerate already-owned by same node, fail loudly on foreign owner
if err := p.occupyCIDR(node, pool, cidr); err != nil {
if errors.Is(err, errAlreadyAllocated) {
// drop stale orphan entry; CIDR owner changed
delete(p.orphans, node)
return nil
}
return fmt.Errorf("unable to mark orphaned CIDR %s still used by node %s as allocated: %w", cidr, node, err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// verify the orphaned CIDR is still in the pool before re-occupying
if !containsCIDR(allocators, cidr) {
delete(p.orphans, node) // stale orphan, drop it
return nil
} Type guard
func orphanOwnedByOtherNode(p *PoolAllocator, cidr netip.Prefix, node string) bool {
owner := p.cidrOwner(cidr)
return owner != "" && owner != node
} Try / catch
if err := reconcileOrphanCIDRs(ctx, p, nodes); err != nil {
var owned *OwnershipConflict
if strings.Contains(err.Error(), "orphaned CIDR") && strings.Contains(err.Error(), "already been allocated") {
// CIDR reassigned while orphaned; drop stale record and requeue
dropStaleOrphans(p)
return nil
}
return err
} Prevention
- Persist allocation state (or run a single leader-elected operator) so restarts don't orphan CIDRs.
- After operator restart, compare node-reported CIDRs with allocator state before reclaiming.
- Freeze pool CIDR edits while orphans are outstanding.
When it happens
Trigger: unorphanCIDR(node, pool, cidr) is invoked and occupyCIDR returns errAlreadyAllocated (another node took the CIDR while it was orphaned), 'cidr is not part of the requested pool' (pool CIDR config changed), or an allocator error. reconcileOrphanCIDRs then surfaces the wrapped error.
Common situations: Operator restarted with lost in-memory allocation state while nodes kept their CIDRs; a competing operator instance re-allocated the same CIDR; pool CIDRs were edited during the orphan window so the orphaned prefix is no longer in range.
Related errors
- not ready
- no cilium agent pods found
- unable to detect minimum Cilium version
- unable to initialize IPv4 allocator: %w
- unable to initialize IPv6 allocator: %w
AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31).
Data as JSON: /api/errors/c8d497f70a1f9a6e.
Report an issue: GitHub.