{"record":{"id":"396fe2bfea24f40d","repo":"router-for-me/CLIProxyAPI","slug":"failed-to-read-body","errorCode":null,"errorMessage":"failed to read body","messagePattern":"failed to read body","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"internal/api/handlers/management/auth_files_crud.go","lineNumber":292,"sourceCode":"\t}\n\tif err := h.upsertAuthRecord(ctx, auth); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc requestedAuthFileNamesForDelete(c *gin.Context) ([]string, error) {\n\tif c == nil {\n\t\treturn nil, nil\n\t}\n\tnames := uniqueAuthFileNames(c.QueryArray(\"name\"))\n\tif len(names) > 0 {\n\t\treturn names, nil\n\t}\n\n\tbody, err := io.ReadAll(c.Request.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read body\")\n\t}\n\tbody = bytes.TrimSpace(body)\n\tif len(body) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tvar objectBody struct {\n\t\tName  string   `json:\"name\"`\n\t\tNames []string `json:\"names\"`\n\t}\n\tif body[0] == '[' {\n\t\tvar arrayBody []string\n\t\tif err := json.Unmarshal(body, &arrayBody); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid request body\")\n\t\t}\n\t\treturn uniqueAuthFileNames(arrayBody), nil\n\t}\n\tif err := json.Unmarshal(body, &objectBody); err != nil {","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/api/handlers/management/auth_files_crud.go#L274-L310","documentation":"requestedAuthFileNamesForDelete accepts deletion targets via query params (`?name=...`) or, failing that, by reading the request body. If io.ReadAll on the body errors it returns `failed to read body` — the connection failed while reading the (optional) JSON body carrying name/names for the delete request. Note the underlying read error is deliberately dropped from the message.","triggerScenarios":"DELETE request whose body transfer aborts mid-read (client disconnect, connection reset, proxy timeout); malformed chunked encoding; client sending a body with a bad Content-Length.","commonSituations":"REST clients that stream a body then abort; intermediary proxies resetting long-lived connections; automation retrying a DELETE while the previous socket is half-closed.","solutions":["Retry the delete request — body read failures are almost always connection-level and transient.","Prefer the query-parameter form (?name=auth.json) which needs no body at all.","Ensure the client sends Content-Length correctly or uses no body for DELETE.","Check for proxy/LB request timeouts between client and management API."],"exampleFix":"# before: body-based delete that can fail mid-read\n$ curl -X DELETE http://127.0.0.1:8000/v0/management/auth-files -d '{\"name\":\"a.json\"}'\n\n# after: query-param delete, no body\n$ curl -X DELETE 'http://127.0.0.1:8000/v0/management/auth-files?name=a.json'","handlingStrategy":"retry","validationCode":null,"typeGuard":"func isBodyReadFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to read body\")\n}","tryCatchPattern":"if isBodyReadFailure(err) {\n    // prefer query-param delete which needs no body\n    err = deleteViaQueryParams(names)\n}","preventionTips":["Prefer ?name= query parameters for DELETE calls — no body to fail.","Ensure clients send correct Content-Length or an empty body.","Retry once; mid-read aborts are transient."],"tags":["management-api","http","request-body"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}