{"record":{"id":"f3449d9b165d1228","repo":"ory/hydra","slug":"unable-to-read-body","errorCode":null,"errorMessage":"unable to read body","messagePattern":"unable to read body","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/decoderx/http.go","lineNumber":294,"sourceCode":"\t} else if httpx.HasContentType(r, httpContentTypeMultipartForm, httpContentTypeURLEncodedForm) {\n\t\treturn decodeForm(r, destination, c)\n\t}\n\n\treturn errors.WithStack(herodot.ErrInternalServerError().WithReasonf(\"Unable to determine decoder for content type: %s\", r.Header.Get(\"Content-Type\")))\n}\n\nfunc requestBody(r *http.Request, o *httpDecoderOptions) (reader io.ReadCloser, err error) {\n\tif strings.ToUpper(r.Method) == \"GET\" {\n\t\treturn io.NopCloser(bytes.NewBufferString(r.URL.Query().Encode())), nil\n\t}\n\n\tif !o.keepRequestBody {\n\t\treturn r.Body, nil\n\t}\n\n\tbodyBytes, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"unable to read body\")\n\t}\n\n\t_ = r.Body.Close() //  must close\n\tr.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))\n\n\treturn io.NopCloser(bytes.NewBuffer(bodyBytes)), nil\n}\n\nfunc decodeJSONForm(r *http.Request, destination interface{}, o *httpDecoderOptions) error {\n\tif o.jsonSchemaCompiler == nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError().WithReasonf(\"Unable to decode HTTP Form Body because no validation schema was provided. This is a code bug.\"))\n\t}\n\n\tpaths, err := jsonschemax.ListPathsWithRecursion(r.Context(), o.jsonSchemaRef, o.jsonSchemaCompiler, o.maxCircularReferenceDepth)\n\tif err != nil {\n\t\treturn errors.WithStack(herodot.ErrInternalServerError().WithTrace(err).WithReasonf(\"Unable to prepare JSON Schema for HTTP Post Body Form parsing: %s\", err).WithDebugf(\"%+v\", err))\n\t}\n","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/decoderx/http.go#L276-L312","documentation":"requestBody buffers the HTTP request body so it can be re-read by multiple decoders. If io.ReadAll fails (connection reset, client abort, body closed early), the read error is wrapped with 'unable to read body'.","triggerScenarios":"Calling decodeJSON, decodeJSONForm, or decodeForm (via requestBody) when the request Body reader returns an error: client disconnected mid-upload, transport errors, or an already-consumed/closed body without keepRequestBody handling.","commonSituations":"Large JSON payloads where clients time out and abort; proxies terminating requests; tests passing a request with a closed or nil body; reading r.Body twice in custom middleware.","solutions":["Check client-side network stability and increase client/timeout limits for large payloads.","If the body was already read by other middleware, restore it (io.NopCloser(bytes.NewBuffer(bytes))) or rely on keepRequestBody buffering.","In tests, always pass a valid io.Reader to http.NewRequest (e.g. strings.NewReader(\"{}\")) instead of nil."],"exampleFix":"// before\nreq, _ := http.NewRequest(\"POST\", url, nil) // body read fails\n// after\nreq, _ := http.NewRequest(\"POST\", url, strings.NewReader(`{\"key\":\"value\"}`))","handlingStrategy":"try-catch","validationCode":"if r.Body == nil {\n\treturn errors.New(\"request body is nil\")\n}","typeGuard":null,"tryCatchPattern":"if err := decoderx.Decode(ctx, r, &dest, opts); err != nil {\n\tif strings.Contains(err.Error(), \"unable to read body\") {\n\t\treturn http.StatusBadRequest // client aborted or body unreadable\n\t}\n\treturn http.StatusInternalServerError\n}","preventionTips":["Never read r.Body before the decoder unless you restore it with io.NopCloser(bytes.NewBuffer(...)).","Set reasonable client timeouts and request size limits so large uploads aren't aborted.","Return 400 rather than 500 when the body read fails — it's usually a client-side issue."],"tags":["http","request-body","io"],"backgroundTag":"request-body-read-failed","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}