{"record":{"id":"65d82c78950562cf","repo":"geektutu/7days-golang","slug":"nil-getter-65d82c","errorCode":null,"errorMessage":"nil Getter","messagePattern":"nil Getter","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gee-cache/day7-proto-buf/geecache/geecache.go","lineNumber":43,"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\tloader:    &singleflight.Group{},\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]","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-cache/day7-proto-buf/geecache/geecache.go#L25-L61","documentation":"NewGroup panics with 'nil Getter' when the getter argument is nil. A Group requires a Getter as the on-miss callback that loads a cache value from a backing source (e.g. a database); without it a cache miss could never be resolved. This is a fail-fast programmer-error check, not a recoverable runtime failure.","triggerScenarios":"Calling geecache.NewGroup(name, cacheBytes, nil), e.g. when the Getter is built dynamically and a nil is passed in, or a variable holding the intended Getter is zero-valued.","commonSituations":"Structuring a Getter behind an interface variable that was never assigned; wiring configuration where the loader is optional but the cache requires it; copy-pasting NewGroup calls and forgetting the getter argument.","solutions":["Pass a non-nil Getter to NewGroup, e.g. geejdbc.GetterFunc(func(key string) ([]byte, error) {...})","Check program flow that constructs the Getter and ensure it is initialized before NewGroup","If the load is intentionally a no-op, provide a GetterFunc returning an error like errors.New('not found') instead of nil"],"exampleFix":"// before\ng := geecache.NewGroup(\"scores\", 2<<20, nil)\n// after\ng := geecache.NewGroup(\"scores\", 2<<20, geecache.GetterFunc(func(key string) ([]byte, error) {\n    return loadFromDB(key)\n}))","handlingStrategy":"validation","validationCode":"if getter == nil {\n    // build a default or fail before calling NewGroup\n    getter = geecache.GetterFunc(func(key string) ([]byte, error) {\n        return nil, fmt.Errorf(\"no getter configured for %s\", key)\n    })\n}\ng := geecache.NewGroup(\"scores\", 2<<20, getter)","typeGuard":"func hasGetter(g geecache.Getter) bool { return g != nil }","tryCatchPattern":null,"preventionTips":["Always construct Getters explicitly with GetterFunc so nil assignments are visible","Keep getter construction near NewGroup calls in one initialization function","Add unit tests asserting groups are created with non-nil getters"],"tags":["go","panic","cache","programming-error"],"backgroundTag":"nil-getter-panic","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"}