{"record":{"id":"e10bc09053eb7296","repo":"caddyserver/caddy","slug":"reading-request-body-v-e10bc0","errorCode":null,"errorMessage":"reading request body: %v","messagePattern":"reading request body: (.+?)","errorType":"http","errorClass":"caddy.APIError","httpStatus":400,"severity":"error","filePath":"caddyconfig/load.go","lineNumber":89,"sourceCode":"// config that is identical to the currently-running config\n// will be a no-op unless Cache-Control: must-revalidate is set.\nfunc (adminLoad) handleLoad(w http.ResponseWriter, r *http.Request) error {\n\tif r.Method != http.MethodPost {\n\t\treturn caddy.APIError{\n\t\t\tHTTPStatus: http.StatusMethodNotAllowed,\n\t\t\tErr:        fmt.Errorf(\"method not allowed\"),\n\t\t}\n\t}\n\n\tbuf := bufPool.Get().(*bytes.Buffer)\n\tbuf.Reset()\n\tdefer bufPool.Put(buf)\n\n\t_, err := io.Copy(buf, r.Body)\n\tif err != nil {\n\t\treturn caddy.APIError{\n\t\t\tHTTPStatus: http.StatusBadRequest,\n\t\t\tErr:        fmt.Errorf(\"reading request body: %v\", err),\n\t\t}\n\t}\n\tbody := buf.Bytes()\n\n\t// if the config is formatted other than Caddy's native\n\t// JSON, we need to adapt it before loading it\n\tif ctHeader := r.Header.Get(\"Content-Type\"); ctHeader != \"\" {\n\t\tresult, warnings, err := adaptByContentType(ctHeader, body)\n\t\tif err != nil {\n\t\t\treturn caddy.APIError{\n\t\t\t\tHTTPStatus: http.StatusBadRequest,\n\t\t\t\tErr:        err,\n\t\t\t}\n\t\t}\n\t\tif len(warnings) > 0 {\n\t\t\trespBody, err := json.Marshal(warnings)\n\t\t\tif err != nil {\n\t\t\t\tcaddy.Log().Named(\"admin.api.load\").Error(err.Error())","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/load.go#L71-L107","documentation":"Returned with HTTP 400 when io.Copy fails while reading the request body of a POST to the admin API /load endpoint. The body is streamed into a pooled buffer; the copy fails when the connection is broken mid-transfer, the chunked encoding is malformed, or the client aborts. It is a transport-level failure, not a config-content error.","triggerScenarios":"Client disconnects (Ctrl+C, timeout) while a large config is being uploaded; a proxy or load balancer cuts the connection; malformed Content-Length or chunked transfer encoding from a hand-rolled HTTP client; request body larger than an intermediary's body-size cap.","commonSituations":"Uploading a multi-megabyte JSON config through nginx (default 1MB client_max_body_size) where nginx truncates and the client aborts; scripted loads killed by shell timeouts; HTTP/2 downgrade issues in fronting proxies.","solutions":["Retry the request on a stable connection and verify the full body is sent (Content-Length matches file size)","If a reverse proxy fronts the admin endpoint, raise its request body limit (e.g. nginx client_max_body_size)","Load the config from the local filesystem instead (caddy run --config) to bypass network transfer","Check for network instability or MTU/firewall issues between client and admin endpoint"],"exampleFix":"// before\ncurl -m 1 -X POST http://localhost:2019/load --data-binary @huge-config.json\n// after\ncurl -X POST http://localhost:2019/load -H 'Content-Type: application/json' --data-binary @huge-config.json","handlingStrategy":"retry","validationCode":"// pre-flight: body is readable and size is known\nfi, err := os.Stat(cfgPath)\nif err != nil { return err }\nbody, err := os.ReadFile(cfgPath)\nif err != nil { return err }\n_ = fi","typeGuard":null,"tryCatchPattern":"resp, err := doLoad(ctx, body) // POST /load with ctx deadline\nif resp != nil && resp.StatusCode == http.StatusBadRequest {\n    b, _ := io.ReadAll(resp.Body)\n    if strings.Contains(string(b), \"reading request body\") {\n        // transient transport failure: safe to retry with backoff\n        resp, err = retryWithBackoff(ctx, body, 3)\n    }\n}","preventionTips":["Set an explicit generous http.Client timeout matched to config size","Raise reverse-proxy body limits when fronting the admin endpoint","Send bodies with --data-binary / io.ReadFull to avoid truncated uploads"],"tags":["admin-api","network","request-body","http-400"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}