{"record":{"id":"f8a6a8a8391fbbe4","repo":"geektutu/7days-golang","slug":"decoding-response-body-v","errorCode":null,"errorMessage":"decoding response body: %v","messagePattern":"decoding response body: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gee-cache/day7-proto-buf/geecache/http.go","lineNumber":136,"sourceCode":"\t\turl.QueryEscape(in.GetKey()),\n\t)\n\tres, err := http.Get(u)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer res.Body.Close()\n\n\tif res.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"server returned: %v\", res.Status)\n\t}\n\n\tbytes, err := ioutil.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"reading response body: %v\", err)\n\t}\n\n\tif err = proto.Unmarshal(bytes, out); err != nil {\n\t\treturn fmt.Errorf(\"decoding response body: %v\", err)\n\t}\n\n\treturn nil\n}\n\nvar _ PeerGetter = (*httpGetter)(nil)\n","sourceCodeStart":118,"sourceCodeEnd":143,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-cache/day7-proto-buf/geecache/http.go#L118-L143","documentation":"The day7 peer protocol encodes responses as protobuf; httpGetter.Get unmarshals the 200-response body into the out proto.Message with proto.Unmarshal. If the bytes are not a valid encoding of the expected message type, the failure is wrapped as \"decoding response body: %v\". This usually means the peer did not actually send a protobuf payload (wrong endpoint, wrong version, or a non-gee-cache server answering 200 with HTML/JSON).","triggerScenarios":"Hitting a day5/day6 (plain text) peer from a day7 client; basePath pointing at the wrong service that returns 200 with non-protobuf data; corrupted/truncated body that still passed ReadAll; a proxy error page served with 200.","commonSituations":"Rolling upgrades with mixed versions in the hash ring; misconfigured basePath such as \"/\" hitting the app's HTML index; manually constructed test requests against the wrong handler.","solutions":["Confirm both sides of the peer connection use the same protobuf message schema (consistent-cache/v1) and day7 code","Verify the peer URL/basePath resolves to the gee-cache handler, not another endpoint returning 200","Upgrade/downgrade nodes so the entire ring speaks the same protocol","Log a preview of the raw bytes on this error to identify what the peer actually sent"],"exampleFix":"// before\n// client expects protobuf but peer serves plain bytes (day5 node in ring)\nvar out pb.Response\nproto.Unmarshal(bytes, out) // decoding response body: unexpected wire type\n// after\n// keep ring membership homogeneous:\npeers.Add(\"http://node1:8001\", \"http://node2:8001\") // all day7, same .proto schema","handlingStrategy":"type-guard","validationCode":"// cheap structural check before unmarshalling\nif len(bytes) == 0 || bytes[0] > 20 { // protobuf fields start with small tags\n    return fmt.Errorf(\"peer response is not protobuf (%q)\", preview(bytes))\n}","typeGuard":"func looksLikeProto(b []byte) bool {\n    return len(b) > 0 && b[0] < 20 // plausible protobuf field header\n}","tryCatchPattern":"err := g.Get(ctx, key, &out)\nif err != nil {\n    if strings.Contains(err.Error(), \"decoding response body\") {\n        log.Printf(\"peer sent non-protobuf payload; check versions/basePath: %v\", err)\n        return g.getLocally(key) // degrade gracefully\n    }\n    return err\n}","preventionTips":["Keep all nodes on the same .proto schema and codegen version","Never point the peer pool at a non-gee-cache endpoint (200 HTML pages decode-fail)","Pin versions during rolling upgrades; probe peers for protocol capability","Log a hex/preview of the body on decode errors to diagnose instantly"],"tags":["protobuf","serialization","distributed-cache","versioning","go"],"backgroundTag":"protobuf-unmarshal-failed","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"}