{"record":{"id":"b24e86a78512131a","repo":"geektutu/7days-golang","slug":"nil-getter","errorCode":null,"errorMessage":"nil Getter","messagePattern":"nil Getter","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"gee-cache/day2-single-node/geecache/geecache.go","lineNumber":37,"sourceCode":"}\n\n// A GetterFunc implements Getter with a function.\ntype GetterFunc func(key string) ([]byte, error)\n\n// Get implements Getter interface function\nfunc (f GetterFunc) Get(key string) ([]byte, error) {\n\treturn f(key)\n}\n\nvar (\n\tmu     sync.RWMutex\n\tgroups = make(map[string]*Group)\n)\n\n// NewGroup create a new instance of Group\nfunc NewGroup(name string, cacheBytes int64, getter Getter) *Group {\n\tif getter == nil {\n\t\tpanic(\"nil Getter\")\n\t}\n\tmu.Lock()\n\tdefer mu.Unlock()\n\tg := &Group{\n\t\tname:      name,\n\t\tgetter:    getter,\n\t\tmainCache: cache{cacheBytes: cacheBytes},\n\t}\n\tgroups[name] = g\n\treturn g\n}\n\n// GetGroup returns the named group previously created with NewGroup, or\n// nil if there's no such group.\nfunc GetGroup(name string) *Group {\n\tmu.RLock()\n\tg := groups[name]\n\tmu.RUnlock()","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-cache/day2-single-node/geecache/geecache.go#L19-L55","documentation":"geecache.NewGroup panics when the provided cache-miss callback (Getter) is nil, because the group would have no way to fetch values on a cache miss. The library treats this as a programmer error, so it fails immediately at group construction rather than at first Get.","triggerScenarios":"NewGroup(name, cacheBytes, nil) — typically when the getter is a variable that is nil at construction time, or when building groups programmatically where a callback was omitted.","commonSituations":"Conditional getter wiring where a DB/etcd callback failed to initialize; copy-pasted createGroup code passing a nil interface; refactors that removed the fetch function but kept NewGroup.","solutions":["Pass a non-nil Getter implementation (e.g. a function wrapped via GetterFunc) to NewGroup.","Guard the getter variable before calling NewGroup and return a configuration error instead.","Wire a real data-source callback (database, API, etc.) used on cache misses."],"exampleFix":"// before\nvar getter gee Getter // nil\ngeecache.NewGroup(\"scores\", 2<<20, getter)\n// after\ngeecache.NewGroup(\"scores\", 2<<20, geecache.GetterFunc(func(key string) ([]byte, error) {\n    return loadFromDB(key)\n}))","handlingStrategy":"validation","validationCode":"if getter == nil {\n    return errors.New(\"geecache: NewGroup requires a non-nil Getter\")\n}\ng := geecache.NewGroup(name, cacheBytes, getter)","typeGuard":"func hasGetter(g geecache.Getter) bool { return g != nil }\nif !hasGetter(getter) { getter = defaultGetter }","tryCatchPattern":"// NewGroup panics, so guard before calling; if unavoidable:\nfunc safeNewGroup(name string, bytes int64, getter geecache.Getter) (g *geecache.Group) {\n    defer func() {\n        if r := recover(); r != nil && r == \"nil Getter\" {\n            g = geecache.NewGroup(name, bytes, geecache.GetterFunc(defaultLoader))\n        }\n    }()\n    return geecache.NewGroup(name, bytes, getter)\n}","preventionTips":["Always wrap data-source callbacks with GetterFunc and verify non-nil before NewGroup.","Initialize groups in one place (createGroup) so nil wiring is caught early.","Add a startup config check that all fetchers are registered before building groups.","Treat the panic as a boot-time failure: fail fast with a clear config error instead."],"tags":["go","panic","cache","configuration"],"backgroundTag":"nil-callback-configuration","analyzedSha":"cf3644382101dc13e7fd92e8f5c66cabc51bcd3b","analyzedAt":"2026-09-03T18:31:24.087Z","contentChangedAt":"2026-09-03T18:31:24.087Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}