{"record":{"id":"74481a208584daf8","repo":"cilium/cilium","slug":"cidr-s-has-already-been-allocated","errorCode":null,"errorMessage":"cidr %s has already been allocated","messagePattern":"cidr (.+?) has already been allocated","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"operator/pkg/ipam/allocator/multipool/pool.go","lineNumber":41,"sourceCode":"\t\t}\n\n\t\treturn alloc.AllocateNext()\n\t}\n\n\treturn netip.Prefix{}, errPoolEmpty\n}\n\nfunc occupyCIDR(allocators []cidralloc.CIDRAllocator, cidr netip.Prefix) error {\n\tfor _, alloc := range allocators {\n\t\tif !alloc.InRange(cidr) {\n\t\t\tcontinue\n\t\t}\n\t\tallocated, err := alloc.IsAllocated(cidr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif allocated {\n\t\t\treturn fmt.Errorf(\"cidr %s has already been allocated\", cidr)\n\t\t}\n\n\t\treturn alloc.Occupy(cidr)\n\t}\n\n\treturn fmt.Errorf(\"cidr %s is not part of the requested pool\", cidr)\n}\n\nfunc releaseCIDR(allocators []cidralloc.CIDRAllocator, cidr netip.Prefix) error {\n\tfor _, alloc := range allocators {\n\t\tif !alloc.InRange(cidr) {\n\t\t\tcontinue\n\t\t}\n\n\t\tallocated, err := alloc.IsAllocated(cidr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/operator/pkg/ipam/allocator/multipool/pool.go#L23-L59","documentation":"occupyCIDR walks the pool's CIDRAllocators, finds the one whose range contains the given prefix, and refuses to occupy it if IsAllocated reports the prefix already claimed. This guards the multi-pool allocator against double-allocation of a pod CIDR within a pool.","triggerScenarios":"PoolAllocator.occupyCIDR(node, pool, cidr) is called with a prefix that is inside a pool allocator's range but already occupied — e.g. unorphanCIDR reclaiming an orphaned CIDR that another node has since been allocated, or a CiliumNode re-registration racing a fresh allocation of the same CIDR.","commonSituations":"Stale node state: a deleted node's CIDRs were released and re-allocated to another node, then the old node's status (still listing the CIDR) triggers occupy again; two operator replicas running simultaneously; restoring state from an old snapshot.","solutions":["Determine which node currently holds the CIDR (check CiliumNode Status.IPAM / pool allocations) and reconcile the stale node instead of re-occupying.","Ensure only one operator instance runs (leader election enabled) — duplicate allocators cause exactly this double-occupy.","Delete/restart the stale CiliumNode object so the agent re-requests a fresh CIDR from the current pool state.","If it comes from unorphanCIDR, verify the orphaned CIDR is still actually used by that node (check the node's PodCIDRs/pools) before marking it allocated."],"exampleFix":"// before: blindly occupy on node upsert\nif err := p.occupyCIDR(node.Name, pool, cidr); err != nil { ... }\n// after: check current owner first\nif alloc, err := alloc.IsAllocated(cidr); err == nil && alloc {\n\t// skip: CIDR already owned; let node reconcile release or re-request\n\tcontinue\n}\nreturn p.occupyCIDR(node.Name, pool, cidr)","handlingStrategy":"validation","validationCode":"// before occupying, check current ownership\noccupied, err := alloc.IsAllocated(cidr)\nif err != nil { return err }\nif occupied {\n\t// resolve owner first; do not force occupy\n\treturn fmt.Errorf(\"cidr %s owned by another node\", cidr)\n}","typeGuard":"func cidrInPool(allocators []cidralloc.CIDRAllocator, cidr netip.Prefix) bool {\n\tfor _, a := range allocators {\n\t\tif a.InRange(cidr) { return true }\n\t}\n\treturn false\n}","tryCatchPattern":"if err := p.OccupyCIDR(node, pool, cidr); err != nil {\n\tif strings.Contains(err.Error(), \"has already been allocated\") {\n\t\t// reconcile stale node instead of retrying occupy\n\t\treleaseStaleNodeAllocations(node)\n\t\treturn nil\n\t}\n\treturn err\n}","preventionTips":["Run a single operator instance with leader election.","Before re-occupying, check which node currently holds the CIDR in CiliumNode specs.","Clear stale PodCIDRs/pool allocations on node replacement."],"tags":["ipam","cidr","cilium","double-allocation"],"backgroundTag":"cidr-already-allocated","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}