Malformed request body failed to decode: Unexpected end of input
Describe the bug
Method that receives and parses JSON fails from time to time with an error zio.http.codec.HttpCodecError$MalformedBody: Malformed request body failed to decode: Unexpected end of input
JSON content is valid and static (checked in the browser developer tools) but sometimes the page loads successfully, sometimes fails with the error message above.
We're using zio-http 3.3.3 Endpoint API and zio-json 0.7.42 for requesting a server for data. Simplified model is TreeNode(data: Data, children: List[TreeNode]) Response of the request is around 17k bytes.
To Reproduce Steps to reproduce the behaviour:
- Refresh the page that contains this method multiple times
Expected behaviour Page loads successfully, no error messages appear in the console.
Desktop (please complete the following information):
- OS: Windows 11 Pro 24H2
- Browser [Chrome 137.0.7151.104, Firefox 139.0.4]
Additional context Full error message:
timestamp=2025-06-16T08:59:57.725Z level=ERROR thread=#zio-fiber-2481762 message="Error occurred during loadGroups (Invocation(Endpoint(GET /api/groups,Combine(Method(<function1>,0),Path(/api/groups,0),zio.http.codec.Combiner$$anon$1@80),Annotated(Combine(Content(Choices(ListMap(MediaType(application,json,true,false,List(json, map),Map(),Map()) -> BinaryCodecWithSchema(<function1>,Sequence(CaseClass7(Nominal(Chunk(datasapience,licman),Chunk(),GroupDto), Field(id,$Lazy$),Field(name,$Lazy$),Field(description,$Lazy$),Field(groupId,$Lazy$),Field(subgroups,$Lazy$),Field(modules,$Lazy$),Field(metricTemplates,$Lazy$)), List)), MediaType(application,protobuf,false,true,List(),Map(),Map()) -> BinaryCodecWithSchema(<function1>,Sequence(CaseClass7(Nominal(Chunk(datasapience,licman),Chunk(),GroupDto), Field(id,$Lazy$),Field(name,$Lazy$),Field(description,$Lazy$),Field(groupId,$Lazy$),Field(subgroups,$Lazy$),Field(modules,$Lazy$),Field(metricTemplates,$Lazy$)), List)), MediaType(text,plain,true,false,List(txt, text, conf, def, list, log, in, ini),Map(),Map()) -> BinaryCodecWithSchema(<function1>,Sequence(CaseClass7(Nominal(Chunk(datasapience,licman),Chunk(),GroupDto), Field(id,$Lazy$),Field(name,$Lazy$),Field(description,$Lazy$),Field(groupId,$Lazy$),Field(subgroups,$Lazy$),Field(modules,$Lazy$),Field(metricTemplates,$Lazy$)), List)), MediaType(application,octet-stream,false,true,List(bin, dms, lrf, mar, so, dist, distz, pkg, bpk, dump, elc, deploy, exe, dll, deb, dmg, iso, img, msi, msp, msm, buffer),Map(),Map()) -> BinaryCodecWithSchema(<function1>,Sequence(CaseClass7(Nominal(Chunk(datasapience,licman),Chunk(),GroupDto), Field(id,$Lazy$),Field(name,$Lazy$),Field(description,$Lazy$),Field(groupId,$Lazy$),Field(subgroups,$Lazy$),Field(modules,$Lazy$),Field(metricTemplates,$Lazy$)), List)))),None,0),Status(<function1>,0),zio.http.codec.CombinerLowPriority1$$anon$2@81),Documented(Paragraph(Text(Found groups with its modules)))),TransformOrFail(TransformOrFail(Fallback(TransformOrFail(Fallback(Combine(Content(Choices(ListMap(MediaType(application,json,true,false,List(json, map),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass0(Nominal(Chunk(datasapience,licman),Chunk(),NotAuthenticated), )), MediaType(application,protobuf,false,true,List(),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass0(Nominal(Chunk(datasapience,licman),Chunk(),NotAuthenticated), )), MediaType(text,plain,true,false,List(txt, text, conf, def, list, log, in, ini),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass0(Nominal(Chunk(datasapience,licman),Chunk(),NotAuthenticated), )), MediaType(application,octet-stream,false,true,List(bin, dms, lrf, mar, so, dist, distz, pkg, bpk, dump, elc, deploy, exe, dll, deb, dmg, iso, img, msi, msp, msm, buffer),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass0(Nominal(Chunk(datasapience,licman),Chunk(),NotAuthenticated), )))),Some(error-response),0),Status(<function1>,0),zio.http.codec.CombinerLowPriority1$$anon$2@82),Combine(Content(Choices(ListMap(MediaType(application,json,true,false,List(json, map),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass0(Nominal(Chunk(datasapience,licman),Chunk(),NotAuthorized), )), MediaType(application,protobuf,false,true,List(),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass0(Nominal(Chunk(datasapience,licman),Chunk(),NotAuthorized), )), MediaType(text,plain,true,false,List(txt, text, conf, def, list, log, in, ini),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass0(Nominal(Chunk(datasapience,licman),Chunk(),NotAuthorized), )), MediaType(application,octet-stream,false,true,List(bin, dms, lrf, mar, so, dist, distz, pkg, bpk, dump, elc, deploy, exe, dll, deb, dmg, iso, img, msi, msp, msm, buffer),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass0(Nominal(Chunk(datasapience,licman),Chunk(),NotAuthorized), )))),Some(error-response),0),Status(<function1>,0),zio.http.codec.CombinerLowPriority1$$anon$2@83),zio.http.codec.AlternatorLowPriority3$$anon$4@84,IsHttpCodecError),<function1>,<function1>),Combine(Content(Choices(ListMap(MediaType(application,json,true,false,List(json, map),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass1(Field(msg,$Lazy$))), MediaType(application,protobuf,false,true,List(),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass1(Field(msg,$Lazy$))), MediaType(text,plain,true,false,List(txt, text, conf, def, list, log, in, ini),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass1(Field(msg,$Lazy$))), MediaType(application,octet-stream,false,true,List(bin, dms, lrf, mar, so, dist, distz, pkg, bpk, dump, elc, deploy, exe, dll, deb, dmg, iso, img, msi, msp, msm, buffer),Map(),Map()) -> BinaryCodecWithSchema(<function1>,CaseClass1(Field(msg,$Lazy$))))),Some(error-response),0),Status(<function1>,0),zio.http.codec.CombinerLowPriority1$$anon$2@85),zio.http.codec.AlternatorLowPriority3$$anon$4@86,IsHttpCodecError),<function1>,<function1>),<function1>,<function1>),Combine(Content(Choices(ListMap(MediaType(text,html,true,false,List(html, htm, shtml),Map(),Map()) -> BinaryCodecWithSchema(<function1>,Transform(Transform(Primitive(string,Chunk()), SourceLocation(/home/runner/work/zio-http/zio-http/zio-http/shared/src/main/scala/zio/http/template/Dom.scala,89,56)), SourceLocation(/home/runner/work/zio-http/zio-http/zio-http/shared/src/main/scala/zio/http/codec/HttpContentCodec.scala,263,5))), MediaType(application,json,true,false,List(json, map),Map(),Map()) -> BinaryCodecWithSchema(<function1>,Transform(CaseClass2(Nominal(Chunk(zio,http,codec),Chunk(HttpContentCodec),DefaultCodecError), Field(name,$Lazy$),Field(message,$Lazy$)), SourceLocation(/home/runner/work/zio-http/zio-http/zio-http/shared/src/main/scala/zio/http/codec/HttpContentCodec.scala,272,5))))),None,0),Status(<function1>,0),zio.http.codec.CombinerLowPriority1$$anon$2@87),Paragraph(Text(Retrieves all available groups with its modules)),Bearer),undefined))" cause="zio.http.codec.HttpCodecError$MalformedBody: Malformed request body failed to decode: Unexpected end of input
" location=datasapience.licman.SecuredServer.loadGroups file=SecuredServer.scala line=35 RuntimePlatformSpecific.scala:59:44
BY RuntimePlatformSpecific.scala:59
BY ZLogger.scala:76
t3E FiberRuntime.scala:794
tN$/< ZIO.scala:4209
pa app.d46bbcd7.js:1
t FiberRuntime.scala:1084
t FiberRuntime.scala:1067
t3w FiberRuntime.scala:412
e FiberRuntime.scala:487
OB FiberRuntime.scala:137
UY MacrotaskExecutor.scala:32
ca WrappedDictionary.scala:192
t MacrotaskExecutor.scala:74
tAP MacrotaskExecutor.scala:118
tAF MacrotaskExecutor.scala:123
p app.d46bbcd7.js:1
tlY Any.scala:146
Please provide code for a reproducer
Hello,
With this 'small' example, I can reproduce the bug.
There is a server you can execute with sbt run and a client you execute with npm install ; npm run dev
Open firefox on http://localhost:5173/ and then set the throttling to Regular 2G in the web dev tools.
After clicking on the button, in the console you will have the error message
'Failure(Die(zio.http.codec.HttpCodecError$MalformedBody: Malformed request body failed to decode: Unexpected end of input,Stack trace for thread "zio-fiber-":'
To me, it looks like the problem comes from the class zio.http.internal.FetchBody
We are reading a ReadableStream using only one call in asArray method to content.getReader().read(). I was expecting a recursive call to it from asStream.