{"record":{"id":"b8fb4e14521e1ccd","repo":"Netflix/chaosmonkey","slug":"failed-to-close-response-body-of-s","errorCode":null,"errorMessage":"failed to close response body of %s","messagePattern":"failed to close response body of (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spinnaker/terminator.go","lineNumber":89,"sourceCode":"// Execute implements term.Terminator.Execute\nfunc (s Spinnaker) Execute(trm chaosmonkey.Termination) (err error) {\n\tins := trm.Instance\n\turl := s.tasksURL(ins.AppName())\n\n\totherID, err := s.OtherID(ins)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"retrieve other id failed\")\n\t}\n\n\tpayload := killJSONPayload(ins, otherID, s.user)\n\tresp, err := s.client.Post(url, \"application/json\", bytes.NewReader(payload))\n\tif err != nil {\n\t\treturn errors.Wrap(err, fmt.Sprintf(\"POST to %s failed, (body '%s')\", url, string(payload)))\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 of %s\", url))\n\t\t}\n\t}()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tlog.Printf(\"Unexpected response: %d\", resp.StatusCode)\n\t\tcontents, err := ioutil.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn errors.Wrap(err, \"failed to read response body\")\n\t\t}\n\t\treturn fmt.Errorf(\"unexpected response code: %d, body: %s\", resp.StatusCode, string(contents))\n\t}\n\n\treturn nil\n}\n\n// killJsonPayload generates the JSON request body for terminating an instance\n// otherID is an optional second instance ID, as some backends may have a second\n// identifer.","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/Netflix/chaosmonkey/blob/eaa28fb761c0ebe8644d1333e5d164e9cc3071e9/spinnaker/terminator.go#L71-L107","documentation":"This deferred error is raised when resp.Body.Close() returns an error after the POST to the Spinnaker task endpoint — and only when the main flow had no other error (cerr != nil && err == nil). It means the response body could not be closed cleanly, typically indicating an interrupted or abnormal connection state. It replaces the named return value err so the caller still sees a failure even though the termination request itself succeeded.","triggerScenarios":"The POST to <spinnaker-api>/tasks received a response, but calling Close() on resp.Body returned a non-nil error — usually because the underlying connection was already broken/reset while the body was being torn down.","commonSituations":"Keepalive connection reset by Gate or an intermediate LB at body-close time; client timeouts racing with response draining; Go http.Client reusing a connection that the server closed; reading very large error-response bodies earlier in the flow interacting badly with connection state.","solutions":["Treat as mostly benign if the kill succeeded (status 200 was returned); check Gate logs to confirm the task ran.","Read/drain the body fully (ioutil.ReadAll) before Close if the flow doesn't, to make connection reuse clean.","Disable keepalive or close idle connections if resets recur (http.Transport settings).","Inspect errors.Cause for the specific close error and correlate with network/LB logs.","Upgrade Go or adjust http.Transport (IdleConnTimeout, MaxIdleConnsPerHost) to reduce stale keepalive connections."],"exampleFix":"// before\ndefer func() {\n\tif cerr := resp.Body.Close(); cerr != nil && err == nil {\n\t\terr = errors.Wrap(cerr, fmt.Sprintf(\"failed to close response body of %s\", url))\n\t}\n}()\n// after\ndefer func() {\n\tio.Copy(io.Discard, resp.Body) // drain before close\n\tif cerr := resp.Body.Close(); cerr != nil && err == nil {\n\t\terr = errors.Wrap(cerr, fmt.Sprintf(\"failed to close response body of %s\", url))\n\t}\n}()","handlingStrategy":"try-catch","validationCode":"// drain the body before the deferred close to reduce close errors\nresp, err := client.Post(url, \"application/json\", bytes.NewReader(payload))\nif err != nil {\n\treturn err\n}\nio.Copy(io.Discard, resp.Body)","typeGuard":null,"tryCatchPattern":"err := terminateInstance(payload)\nif err != nil {\n\tif strings.Contains(err.Error(), \"failed to close response body\") && killConfirmed(taskID) {\n\t\tlog.Printf(\"ignoring close error; kill task %s confirmed\", taskID)\n\t\treturn nil\n\t}\n\treturn err\n}","preventionTips":["Always read/drain resp.Body before Close for clean connection reuse","Tune http.Transport keepalive settings (IdleConnTimeout) to avoid stale connections","Confirm task success via Spinnaker task status before trusting close-error failures","Log close errors at warning level; they rarely invalidate the termination"],"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"}