{"record":{"id":"6fa6cdd5f0a078bf","repo":"cilium/cilium","slug":"missing-snapshot","errorCode":null,"errorMessage":"missing snapshot","messagePattern":"missing snapshot","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/envoy/xdsnew/cache.go","lineNumber":131,"sourceCode":"\t}\n\tout := make(map[string]cache_types.Resource, len(resources))\n\tfor name, resource := range resources {\n\t\tout[name] = resource.Resource\n\t}\n\treturn out\n}\n\nfunc (w *ciliumSnapshot) GetResourcesAndTTL(typeURL string) map[string]cache_types.ResourceWithTTL {\n\tgroup, ok := w.Resources[typeURL]\n\tif !ok {\n\t\treturn nil\n\t}\n\treturn group.Items\n}\n\nfunc (w *ciliumSnapshot) ConstructVersionMap() error {\n\tif w == nil {\n\t\treturn fmt.Errorf(\"missing snapshot\")\n\t}\n\tif w.VersionMap != nil {\n\t\treturn nil\n\t}\n\n\tw.VersionMap = make(map[string]map[string]string, len(w.Resources))\n\tfor typeURL, group := range w.Resources {\n\t\tif len(group.Items) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tw.VersionMap[typeURL] = make(map[string]string, len(group.Items))\n\t\tfor name, resource := range group.Items {\n\t\t\tmarshaledResource, err := cache.MarshalResource(resource.Resource)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tw.VersionMap[typeURL][name] = cache.HashResource(marshaledResource)\n\t\t}","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/pkg/envoy/xdsnew/cache.go#L113-L149","documentation":"ConstructVersionMap on a ciliumSnapshot returns the sentinel error 'missing snapshot' when the receiver w is nil. The version map is needed by the go-control-plane cache to compute resource versions; a nil snapshot has none. Callers use this as an explicit guard rather than panicking on a nil dereference.","triggerScenarios":"Calling ConstructVersionMap on a nil *ciliumSnapshot — e.g. a snapshot lookup in the cache returned nil (unknown node, removed cache entry) and the code still invoked ConstructVersionMap on it.","commonSituations":"xDS clients requesting resources for node IDs that were never set or already evicted from the snapshot cache; races where a snapshot is deleted between fetch and use; tests constructing cache responses for nonexistent proxies.","solutions":["Verify the node ID used to fetch the snapshot exists in the snapshot cache (SetSnapshot was called for it).","Check for races where RemoveSnapshot/eviction runs while the snapshot is still being used; serialize cache lifecycle operations.","Add a nil check before calling ConstructVersionMap in custom code built around ciliumSnapshot.","If this occurs inside stock Cilium flows, capture logs and file an issue — a nil snapshot reaching ConstructVersionMap indicates a cache lifecycle bug."],"exampleFix":"// before\nerr := snapshot.ConstructVersionMap()\n// after\nif snapshot == nil {\n    return fmt.Errorf(\"no snapshot available for node\")\n}\nerr := snapshot.ConstructVersionMap()","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func snapshotReady(s cache.ResourceSnapshot) bool {\n    cs, ok := s.(*ciliumSnapshot)\n    return ok && cs != nil\n}","tryCatchPattern":"if err := w.ConstructVersionMap(); err != nil {\n    if err.Error() == \"missing snapshot\" {\n        // node has no snapshot; skip response or SetSnapshot first\n        return cache.ErrNoSnapshot\n    }\n    return err\n}","preventionTips":["Always SetSnapshot for a node ID before serving its resources.","Guard lookups: treat nil snapshot results as 'no snapshot' rather than invoking methods.","Serialize snapshot create/remove against consumers to avoid use-after-eviction races."],"tags":["go","xds","cache","nil"],"backgroundTag":"nil-snapshot","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}