{"record":{"id":"dadaf6bd858679a7","repo":"Netflix/chaosmonkey","slug":"failed-to-close-response-body-from-s-dadaf6","errorCode":null,"errorMessage":"failed to close response body from %s","messagePattern":"failed to close response body from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spinnaker/terminator.go","lineNumber":152,"sourceCode":"\t\tlog.Fatalf(\"chronos.jsonPayload could not marshal data into json: %v\", err)\n\t}\n\n\treturn result\n}\n\n// OtherID returns the alternate instance id of an instance, if it exists\n// If there is no alternate instance id, it returns an empty string\n// This is used by Titus, where we also report the uuid\nfunc (s Spinnaker) OtherID(ins chaosmonkey.Instance) (otherID string, err error) {\n\turl := s.instanceURL(ins.AccountName(), ins.RegionName(), ins.ID())\n\tresp, err := s.client.Get(url)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, fmt.Sprintf(\"get failed on %s\", url))\n\t}\n\n\tdefer func() {\n\t\tif cerr := resp.Body.Close(); cerr != nil && err == nil {\n\t\t\terr = errors.Wrap(cerr, fmt.Sprintf(\"failed to close response body from %s\", url))\n\t\t}\n\t}()\n\n\tbody, err := ioutil.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, fmt.Sprintf(\"body read failed at %s\", url))\n\t}\n\n\t// Example of response body:\n\t/*\n\t\t{\n\t\t\t...\n\t\t\t\"health\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"Titus\",\n\t\t\t\t\t\"healthClass\": \"platform\",\n\t\t\t\t\t\"state\": \"Up\"\n\t\t\t\t},","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/Netflix/chaosmonkey/blob/eaa28fb761c0ebe8644d1333e5d164e9cc3071e9/spinnaker/terminator.go#L134-L170","documentation":"This deferred error fires when resp.Body.Close() fails inside OtherID, but only when the rest of OtherID completed without error (cerr != nil && err == nil). The GET for the instance's alternate ID otherwise succeeded; only connection teardown failed. Because OtherID uses a named return, this close error replaces the successful result with an error, which then surfaces wrapped as \"retrieve other id failed\" in Execute.","triggerScenarios":"GET to the Spinnaker instance endpoint succeeded and the body was read, but resp.Body.Close() returned an error — typically a reset/aborted keepalive connection during teardown.","commonSituations":"Server or LB closing keepalive connections aggressively; stale pooled connections in http.Transport; network device interrupting idle connections between request completion and close.","solutions":["Check whether the underlying data was still correct; if so, this is a teardown artifact — drain the body before closing to reduce it.","Use io.Copy(io.Discard, resp.Body) before Close, or ensure full reads (the code already does ioutil.ReadAll, so check for early-return paths).","Tune http.Transport keepalive settings (IdleConnTimeout, MaxIdleConnsPerHost) to avoid stale connections.","Correlate errors.Cause with LB/Gate logs for connection resets.","If it persists and blocks terminations, consider ignoring close errors on GET responses whose body was fully read."],"exampleFix":"// before\nif cerr := resp.Body.Close(); cerr != nil && err == nil {\n\terr = errors.Wrap(cerr, fmt.Sprintf(\"failed to close response body from %s\", url))\n}\n// after\n// body fully read above; treat close error as non-fatal\n_ = resp.Body.Close()","handlingStrategy":"try-catch","validationCode":"// body is fully read via ioutil.ReadAll before close; drain defensively on early-return paths\nio.Copy(io.Discard, resp.Body)","typeGuard":null,"tryCatchPattern":"otherID, err := spinnaker.OtherID(ins)\nif err != nil {\n\tif strings.Contains(err.Error(), \"failed to close response body\") {\n\t\tlog.Printf(\"non-fatal close error looking up %s: %v\", ins.ID(), err)\n\t\t// data was fine; retry or proceed per policy\n\t}\n\treturn errors.Wrap(err, \"retrieve other id failed\")\n}","preventionTips":["Drain resp.Body fully before Close to keep pooled connections healthy","Tune http.Transport (IdleConnTimeout, MaxIdleConnsPerHost) to avoid stale keepalives","Treat close errors on fully-read GET bodies as non-fatal in caller policy","Correlate with LB logs if reset-on-close errors recur"],"tags":["go","http","resource-cleanup","spinnaker"],"backgroundTag":"response-body-close-failed","analyzedSha":"eaa28fb761c0ebe8644d1333e5d164e9cc3071e9","analyzedAt":"2026-09-03T17:04:39.020Z","contentChangedAt":"2026-09-03T17:04:39.020Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}