{"record":{"id":"b283f67e0b3a4ff0","repo":"openfaas/faas","slug":"error-reading-request-body","errorCode":null,"errorMessage":"Error reading request body","messagePattern":"Error reading request body","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"gateway/scaling/ranges.go","lineNumber":42,"sourceCode":"\t// MinScaleLabel label indicating min scale for a function\n\tMinScaleLabel = \"com.openfaas.scale.min\"\n\n\t// MaxScaleLabel label indicating max scale for a function\n\tMaxScaleLabel = \"com.openfaas.scale.max\"\n\n\t// ScalingFactorLabel label indicates the scaling factor for a function\n\tScalingFactorLabel = \"com.openfaas.scale.factor\"\n)\n\nfunc MakeHorizontalScalingHandler(next http.HandlerFunc) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tif r.Method != http.MethodPost {\n\t\t\thttp.Error(w, \"Only POST is allowed\", http.StatusMethodNotAllowed)\n\t\t\treturn\n\t\t}\n\n\t\tif r.Body == nil {\n\t\t\thttp.Error(w, \"Error reading request body\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\tbody, err := io.ReadAll(r.Body)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"Error reading request body\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\tscaleRequest := types.ScaleServiceRequest{}\n\t\tif err := json.Unmarshal(body, &scaleRequest); err != nil {\n\t\t\thttp.Error(w, \"Error unmarshalling request body\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\tif scaleRequest.Replicas < 1 {\n\t\t\tscaleRequest.Replicas = 1\n\t\t}","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/openfaas/faas/blob/8d803bf9e2655aec6a60fd0e25b392839f96af95/gateway/scaling/ranges.go#L24-L60","documentation":"The scale handler in gateway/scaling/ranges.go requires r.Body to be non-nil and returns 400 'Error reading request body' otherwise. Go's net/http server always supplies a non-nil body for inbound requests, so production clients cannot trigger this branch — an empty body passes here and fails later at JSON unmarshal. It is defensive code that mainly fires in unit tests and handcrafted handlers.","triggerScenarios":"Unit tests invoking the handler with a zero-value http.Request (nil Body); custom server plumbing constructing requests without bodies; direct handler invocation in tooling.","commonSituations":"Writing table-driven tests for the scale handler and passing &http.Request{} directly instead of building the request with http.NewRequest or httptest.NewRequest.","solutions":["Always send a JSON body — even {} is valid and replicas clamps to 1","In tests, build requests with http.NewRequest(http.MethodPost, url, bytes.NewReader(body))","Ensure no intermediary strips request bodies before they reach the gateway"],"exampleFix":"// before (unit test)\nreq := httptest.NewRequest(http.MethodPost, \"/system/scale-function/fn\", nil)\n\n// after\nreq := httptest.NewRequest(http.MethodPost, \"/system/scale-function/fn\",\n    strings.NewReader(`{\"replicas\":3}`))","handlingStrategy":"validation","validationCode":"// always attach a body reader before calling the scale endpoint\nif body == nil {\n    body = []byte(\"{}\") // valid: replicas clamps to 1\n}\nreq, _ := http.NewRequest(http.MethodPost, scaleURL, bytes.NewReader(body))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never construct outbound requests without a body reader","In tests prefer httptest.NewRequest with a strings.Reader of JSON","Note that production HTTP servers always deliver a non-nil body — this branch is test-only"],"tags":["http","request-body","scaling","defensive","testing"],"backgroundTag":null,"analyzedSha":"8d803bf9e2655aec6a60fd0e25b392839f96af95","analyzedAt":"2026-08-16T00:12:29.759Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}