{"record":{"id":"c29feb97465bcf35","repo":"cilium/cilium","slug":"unsupported-ipv6-zone-in-address-s","errorCode":null,"errorMessage":"unsupported IPv6 zone in address %s","messagePattern":"unsupported IPv6 zone in address (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/clustermesh/store/store.go","lineNumber":141,"sourceCode":"\n\treturn nil\n}\n\nfunc (s *ClusterService) validate() error {\n\tswitch {\n\tcase s.Cluster == \"\":\n\t\treturn errors.New(\"cluster is unset\")\n\tcase s.Namespace == \"\":\n\t\treturn errors.New(\"namespace is unset\")\n\tcase s.Name == \"\":\n\t\treturn errors.New(\"name is unset\")\n\t}\n\n\tfor address := range s.Frontends {\n\t\tif parsed, err := netip.ParseAddr(address); err != nil {\n\t\t\treturn err\n\t\t} else if parsed.Zone() != \"\" {\n\t\t\treturn fmt.Errorf(\"unsupported IPv6 zone in address %s\", address)\n\t\t}\n\t}\n\n\tfor address := range s.Backends {\n\t\tif parsed, err := netip.ParseAddr(address); err != nil {\n\t\t\treturn err\n\t\t} else if parsed.Zone() != \"\" {\n\t\t\treturn fmt.Errorf(\"unsupported IPv6 zone in address %s\", address)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// BackendZone locates the backend to a specific zone and specifies what zones\n// the backend should be used in for topology aware routing.\n//\n// WARNING - STABLE API: Changing the structure or values of this will","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/pkg/clustermesh/store/store.go#L123-L159","documentation":"When validating a stored GlobalService/CMAP entry, every frontend address must parse as a bare netip.Addr. If the address includes an IPv6 zone (e.g. fe80::1%eth0), parsing succeeds but the zone is rejected because zoned addresses are meaningless across clusters. validate returns this error so the entry is rejected before being published.","triggerScenarios":"A CiliumGlobalServiceEndpoint/GlobalService whose Frontends map contains an IPv6 address with a zone suffix (%eth0, %ens1), typically copied from a node-local scoped address.","commonSituations":"Users copying link-local addresses from ip -6 output into global service configuration; automation exporting zoned addresses from a single node; misconfigured IPv6 frontends on hosts with multiple interfaces.","solutions":["Remove the zone suffix from the frontend address (use fe80::1, not fe80::1%eth0).","Use a globally-routable IPv6 address or a specific ULA instead of a link-local/zoned address as a frontend.","Fix the automation/source that generates frontends to strip or reject zone identifiers."],"exampleFix":"// before\nfrontends: {\"fe80::1%eth0\": {\"80\": {\"p1\": 80}}}\n// after\nfrontends: {\"fd00::10\": {\"80\": {\"p1\": 80}}}","handlingStrategy":"validation","validationCode":"addr, err := netip.ParseAddr(frontend)\nif err != nil || addr.Zone() != \"\" {\n    return fmt.Errorf(\"frontend %s must be a zone-free IP address\", frontend)\n}","typeGuard":"func isZoneFreeIP(s string) bool {\n    a, err := netip.ParseAddr(s)\n    return err == nil && a.Zone() == \"\"\n}","tryCatchPattern":"if err := store.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"unsupported IPv6 zone\") {\n        log.WithError(err).Warn(\"dropping entry with zoned IPv6 address\")\n        return nil // skip entry\n    }\n    return err\n}","preventionTips":["Strip zone identifiers before storing any address (netip.ParseAddr then re-serialize).","Reject link-local addresses as global-service frontends at config-admission time.","Lint generated configs for '%' in IP fields."],"tags":["ipv6","validation","clustermesh","address-format"],"backgroundTag":"ipv6-zone-not-supported","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}