{"record":{"id":"f0eb3283cdbdb3ac","repo":"Netflix/chaosmonkey","slug":"failed-to-read-response-body","errorCode":null,"errorMessage":"failed to read response body","messagePattern":"failed to read response body","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"spinnaker/terminator.go","lineNumber":97,"sourceCode":"\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.\nfunc killJSONPayload(ins chaosmonkey.Instance, otherID string, spinnakerUser string) []byte {\n\tvar desc string\n\tif otherID != \"\" {\n\t\tdesc = fmt.Sprintf(\"Chaos Monkey terminate instance: %s %s (%s, %s, %s)\", ins.ID(), otherID, ins.AccountName(), ins.RegionName(), ins.ASGName())\n\t} else {\n\t\tdesc = fmt.Sprintf(\"Chaos Monkey terminate instance: %s (%s, %s, %s)\", ins.ID(), ins.AccountName(), ins.RegionName(), ins.ASGName())\n\t}\n","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/Netflix/chaosmonkey/blob/eaa28fb761c0ebe8644d1333e5d164e9cc3071e9/spinnaker/terminator.go#L79-L115","documentation":"When the Spinnaker task POST returns a non-200 status, Execute tries to read the error response body to include in a more detailed message. This error is thrown if that ioutil.ReadAll fails, meaning chaosmonkey saw a failed HTTP status but could not even read the error body. The status code was already logged via log.Printf, but the detailed body-based error is lost.","triggerScenarios":"POST <spinnaker-api>/tasks returned a status other than 200, and then reading resp.Body failed with a non-EOF error — connection reset while reading the error response, or a truncated chunked error response.","commonSituations":"Gate or an LB rejecting the kill request (4xx/5xx) and dropping the connection before the error body completed; auth proxies terminating connections on authorization failures; Gate crash mid-response while rejecting a task.","solutions":["Check the log line \"Unexpected response: %d\" printed just before this error for the actual status code.","Read and drain the body with a bounded reader (io.LimitReader) and tolerate partial reads to keep the status-based error path robust.","Investigate why Spinnaker rejected the task (permissions, invalid payload, Gate errors) using Gate logs.","Retry the termination if the status suggests a transient gateway error (502/503/504).","Confirm chaosmonkey's Spinnaker credentials/roles allow task creation for the target application."],"exampleFix":"// before\ncontents, err := ioutil.ReadAll(resp.Body)\nif err != nil {\n\treturn errors.Wrap(err, \"failed to read response body\")\n}\n// after\ncontents, readErr := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))\nif readErr != nil {\n\treturn fmt.Errorf(\"unexpected response code: %d, (body read failed: %v)\", resp.StatusCode, readErr)\n}","handlingStrategy":"fallback","validationCode":"if resp.StatusCode != http.StatusOK {\n\tcontents, readErr := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))\n\tif readErr != nil {\n\t\t// fall back to the status code alone\n\t\treturn fmt.Errorf(\"unexpected response code: %d (body unavailable)\", resp.StatusCode)\n\t}\n\treturn fmt.Errorf(\"unexpected response code: %d, body: %s\", resp.StatusCode, string(contents))\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n\t// fall back to status-code-only error; the code was already logged\n\tlog.Printf(\"failed to read response body: %v (status was %d)\", err, resp.StatusCode)\n\treturn fmt.Errorf(\"unexpected response code: %d\", resp.StatusCode)\n}","preventionTips":["Always include the HTTP status in errors even when the body can't be read","Drain bodies with io.LimitReader to bound memory and tolerate truncation","Watch the \"Unexpected response: %d\" log for Gate rejections (auth, invalid payload)","Retry on 502/503/504 gateway statuses"],"tags":["go","http","io","spinnaker"],"backgroundTag":"http-body-read-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"}