{"record":{"id":"c1f398a5d5abbec2","repo":"geektutu/7days-golang","slug":"key-is-required-c1f398","errorCode":null,"errorMessage":"key is required","messagePattern":"key is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gee-cache/day6-single-flight/geecache/geecache.go","lineNumber":68,"sourceCode":"\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]\n\tmu.RUnlock()\n\treturn g\n}\n\n// Get value for a key from cache\nfunc (g *Group) Get(key string) (ByteView, error) {\n\tif key == \"\" {\n\t\treturn ByteView{}, fmt.Errorf(\"key is required\")\n\t}\n\n\tif v, ok := g.mainCache.get(key); ok {\n\t\tlog.Println(\"[GeeCache] hit\")\n\t\treturn v, nil\n\t}\n\n\treturn g.load(key)\n}\n\n// RegisterPeers registers a PeerPicker for choosing remote peer\nfunc (g *Group) RegisterPeers(peers PeerPicker) {\n\tif g.peers != nil {\n\t\tpanic(\"RegisterPeerPicker called more than once\")\n\t}\n\tg.peers = peers\n}\n","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-cache/day6-single-flight/geecache/geecache.go#L50-L86","documentation":"Identical guard to the day5 version, in the day6-single-flight build: Group.Get rejects an empty key before consulting mainCache. With single-flight added, an empty key would otherwise be a cache miss funneled through a flight group and invoke the loader for a meaningless key, so the early validation also avoids pointless loader/peer work.","triggerScenarios":"group.Get(\"\") on a day6 Group; empty key produced by handler parsing of the URL path (missing /<name>/<key> key segment after trimming the basePath).","commonSituations":"Requests like GET /_geecache/cachename/ (empty key part) forwarded into Get; keys taken from unvalidated request inputs; tests calling Get with placeholder empty strings.","solutions":["Validate/trim the key at the API boundary (HTTP handler) before calling group.Get","Fix the path-parsing or parameter-extraction code that yields an empty key","Return a 400 to the client when the key segment is missing"],"exampleFix":"// before\nparts := strings.SplitN(r.URL.Path[len(p.basePath):], \"/\", 2)\nkey := parts[1]\nv, _ := g.Get(key)\n// after\nif len(parts) != 2 || parts[1] == \"\" {\n    http.Error(w, \"key is required\", http.StatusBadRequest)\n    return\n}\nv, _ := g.Get(parts[1])","handlingStrategy":"validation","validationCode":"key = strings.TrimSpace(key)\nif key == \"\" {\n    http.Error(w, \"missing key\", http.StatusBadRequest)\n    return\n}\nv, err := group.Get(key)","typeGuard":"func nonEmpty(s string) bool { return strings.TrimSpace(s) != \"\" }","tryCatchPattern":null,"preventionTips":["Parse /_geecache/<group>/<key> defensively: reject requests whose key segment is empty with 400","Centralize key construction in one helper with validation","Test edge cases: trailing slashes, URL-encoded empty values, whitespace-only keys"],"tags":["cache","input-validation","go"],"backgroundTag":"empty-cache-key","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"}