{"record":{"id":"ecc95e49f56bb82d","repo":"thanos-io/thanos","slug":"error-sending-proto-response-v","errorCode":null,"errorMessage":"error sending proto response: %v","messagePattern":"error sending proto response: (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"warning","filePath":"internal/cortex/util/http.go","lineNumber":268,"sourceCode":"\treturn nil, false\n}\n\n// SerializeProtoResponse serializes a protobuf response into an HTTP response.\nfunc SerializeProtoResponse(w http.ResponseWriter, resp proto.Message, compression CompressionType) error {\n\tdata, err := proto.Marshal(resp)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn fmt.Errorf(\"error marshaling proto response: %v\", err)\n\t}\n\n\tswitch compression {\n\tcase NoCompression:\n\tcase RawSnappy:\n\t\tdata = snappy.Encode(nil, data)\n\t}\n\n\tif _, err := w.Write(data); err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn fmt.Errorf(\"error sending proto response: %v\", err)\n\t}\n\treturn nil\n}\n","sourceCodeStart":250,"sourceCodeEnd":273,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/internal/cortex/util/http.go#L250-L273","documentation":"SerializeProtoResponse writes a snappy-compressed (or raw) protobuf payload to an http.ResponseWriter. If the underlying Write fails, it also attempts an http.Error fallback and returns this wrapped error. It signals that the client never received a complete proto response because the connection or writer broke mid-send.","triggerScenarios":"Calling SerializeProtoResponse (directly or via an HTTP handler that returns protobuf data) when the client has disconnected, the connection has timed out, or the ResponseWriter has already been used/committed before this call.","commonSituations":"Slow clients hitting server write timeouts; clients canceling queries (common with Grafana/Prometheus query cancellations); reverse proxies closing upstream connections; handlers that already wrote headers or another error before serializing the proto response.","solutions":["Inspect server logs for the wrapped %v cause (broken pipe, connection reset, timeout) to confirm client-side disconnects vs server misconfiguration.","Verify no handler code path writes to the ResponseWriter before SerializeProtoResponse (double-write commits headers early).","Check proxy/load-balancer idle and read timeouts versus slow query durations.","Catch the returned error in the handler and rely on it being logged; the 500 fallback is already sent to the client."],"exampleFix":"// before\nif err := util.SerializeProtoResponse(w, resp, compression); err != nil {\n    level.Error(logger).Log(\"msg\", \"serialize failed\", \"err\", err)\n}\n// after\nif err := util.SerializeProtoResponse(w, resp, compression); err != nil {\n    if errors.Is(err, syscall.EPIPE) || errors.Is(err, syscall.ECONNRESET) {\n        level.Debug(logger).Log(\"msg\", \"client disconnected while sending proto response\", \"err\", err)\n    } else {\n        level.Error(logger).Log(\"msg\", \"serialize failed\", \"err\", err)\n    }\n}","handlingStrategy":"try-catch","validationCode":"// before writing, ensure headers not yet sent and response body supports writes\nif w.Header().Get(\"Content-Type\") == \"\" { w.Header().Set(\"Content-Type\", contentTypeProtobuf) }","typeGuard":null,"tryCatchPattern":"if err := util.SerializeProtoResponse(w, resp, comp); err != nil {\n    var opErr *net.OpError\n    if errors.As(err, &opErr) { // client-side network failure: log at debug, no retry on committed response\n        level.Debug(logger).Log(\"err\", err)\n    } else {\n        level.Error(logger).Log(\"err\", err)\n    }\n}","preventionTips":["Never write to the ResponseWriter before SerializeProtoResponse.","Set realistic WriteTimeouts that accommodate large compressed responses.","Monitor broken-pipe/connection-reset metrics per endpoint.","Keep client keep-alives and proxies configured for long queries."],"tags":["http","network","protobuf","broken-pipe"],"backgroundTag":"broken-pipe","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}