{"record":{"id":"8be1527df24247a6","repo":"geektutu/7days-golang","slug":"bad-request","errorCode":null,"errorMessage":"bad request","messagePattern":"bad request","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"gee-cache/day3-http-server/geecache/http.go","lineNumber":41,"sourceCode":"\t\tbasePath: defaultBasePath,\n\t}\n}\n\n// Log info with server name\nfunc (p *HTTPPool) Log(format string, v ...interface{}) {\n\tlog.Printf(\"[Server %s] %s\", p.self, fmt.Sprintf(format, v...))\n}\n\n// ServeHTTP handle all http requests\nfunc (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tif !strings.HasPrefix(r.URL.Path, p.basePath) {\n\t\tpanic(\"HTTPPool serving unexpected path: \" + r.URL.Path)\n\t}\n\tp.Log(\"%s %s\", r.Method, r.URL.Path)\n\t// /<basepath>/<groupname>/<key> required\n\tparts := strings.SplitN(r.URL.Path[len(p.basePath):], \"/\", 2)\n\tif len(parts) != 2 {\n\t\thttp.Error(w, \"bad request\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tgroupName := parts[0]\n\tkey := parts[1]\n\n\tgroup := GetGroup(groupName)\n\tif group == nil {\n\t\thttp.Error(w, \"no such group: \"+groupName, http.StatusNotFound)\n\t\treturn\n\t}\n\n\tview, err := group.Get(key)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-cache/day3-http-server/geecache/http.go#L23-L59","documentation":"geecache's HTTPPool.ServeHTTP only understands URLs of the form /<basePath>/<groupName>/<key>. When the path after basePath splits on '/' into fewer than 2 segments, it responds with HTTP 400 and body 'bad request'. It is the library telling you the request URL does not match the expected cache API shape.","triggerScenarios":"An HTTP request to the pool whose path after basePath contains fewer than two '/'-separated parts, e.g. GET /_geecache/ or GET /_geecache/mygroup (missing the /<key> segment).","commonSituations":"Health checks or load balancers hitting the pool's base path directly; a client forgetting to append /<key>; proxy rewriting that strips part of the path; typos like a trailing-slash-only URL.","solutions":["Send the full expected path: /<basePath>/<groupName>/<key>, e.g. GET /_geecache/groups/key1.","Exclude this endpoint from health checks, or point health checks at a dedicated route (e.g. /healthz) served by a different handler.","Check any reverse-proxy/ingress rewrite rules so they do not strip the group or key segment.","URL-encode the key if it can contain '/' so SplitN still yields exactly two parts."],"exampleFix":"// before: GET /_geecache/mygroup  -> 400\nhttp.Get(\"http://localhost:8001/_geecache/mygroup\")\n// after: include key\nhttp.Get(\"http://localhost:8001/_geecache/mygroup/key1\")","handlingStrategy":"validation","validationCode":"// validate the URL before calling the cache node\nfunc validCacheURL(base, group, key string) bool {\n    return group != \"\" && key != \"\" && !strings.ContainsAny(key, \"/?#\")\n}\n// url := base + \"/\" + group + \"/\" + url.PathEscape(key)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always build cache URLs as basePath + \"/\" + group + \"/\" + escaped key — never hand-concatenate partial paths.","Point health checks/load balancer probes at a separate route, not the geecache HTTPPool.","Escape keys containing '/' with url.PathEscape before embedding them in the URL.","Add an integration test that asserts 200 for a representative /group/key request."],"tags":["http","cache","bad-request","url-parsing"],"backgroundTag":"malformed-request-path","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"}