Hono cloneRawRequest: stale Content-Length after multipart re-encoding
A Hono fix re-encoded cached FormData but kept the old Content-Length. A Warpway replay of the PR flags it; a contributor fixed it five weeks after merge.
Published 2 min readReplayed through Warpway on By the Warpway team
Replay vs. what happened
Warpway replay
Flags it, given only what existed before merge
Fix opened (#5282)
36 days after merge
Flagged: Forwarded multipart clones keep a Content-Length that no longer matches the body
Replayed on 2026-10-07. Warpway was not installed on honojs/hono when this pull request was open.
Hono's cloneRawRequest() rebuilds a Request after the body has already been read, so a handler can validate a request and then forward it, for example to an upstream service. In July 2026, honojs/hono#5133 fixed a real bug in it. When the cached body was FormData, the clone kept the original multipart Content-Type, whose boundary no longer matched the re-encoded body. The fix deletes that header and lets the Request constructor generate a new one.
On 2026-10-07 we replayed the pull request through Warpway as it stood before merge. In that replay, Warpway reports that a second header goes stale for the same reason, and that URL-encoded forms change type when cloned. Two different contributors found each of those independently, five weeks later.
Key facts
- Pull request
- honojs/hono#5133
- Merged
- 2026-07-17
- Shipped in
- 4.12.31
- Affected
- 4.12.31 to 4.13.3
- Fixed in
- 4.13.4 (#5282)
- Language
- TypeScript
What Warpway flags in the replay
- CorrectnessIssue found
- SecurityCleared
- Testing / ReliabilityCleared
- Architecture / MaintainabilityCleared
- Backward CompatibilityIssue found
- PerformanceCleared
- Product SemanticsCleared
- MediumCorrectnesssrc/request.ts:489
Re-serialized multipart clones retain the original Content-Length
When a consumed multipart request has a Content-Length header (as Hono's Lambda adapters create), cloneRawRequest converts its cached FormData into a new multipart body with a fresh boundary but copies the original Content-Length unchanged. For an incoming body with a different-length boundary, the cloned body's byte count differs from the advertised length. A forwarded clone therefore carries an inconsistent HTTP message and may be rejected or misread by a length-sensitive recipient. The existing multipart test omits Content-Length; Hono's method-override path already removes it when rebuilding FormData.
- MediumBackward Compatibilitysrc/request.ts:489
Cloning a consumed URL-encoded form changes its media type
A POST with
application/x-www-form-urlencodedcan be consumed throughHonoRequest.formData(), leaving a FormData value in the cache.cloneRawRequest()now deletes its original Content-Type and passes that FormData to the Request constructor, which labels the clonemultipart/form-datarather than preserving the source media type.
Warpway saw the pull request as it stood before merge: final code, title, description and commits, without review comments or later history. Findings are quoted verbatim from the recorded review. Several lenses can report the same issue; Warpway merges them, and the first paragraph is shown.
The change
const body = await req[cacheKey]();
const headers = req.header();
if (body instanceof FormData) {
// The FormData is re-serialized with a fresh multipart boundary, so the original
// Content-Type header no longer matches. Let the Request constructor generate it.
delete headers['content-type'];
}How the bug works
Re-serializing FormData produces a brand-new multipart body: a new random boundary, new part headers and, almost always, a different number of bytes. The fix removed Content-Type because the boundary changed, but kept Content-Length, which describes the old body's size. Any request that arrived with a Content-Length header is forwarded with a length that is wrong. Clients send one for ordinary form posts, and Hono's AWS Lambda adapter sets one.
In our test on Hono 4.13.3, a 63-byte multipart request cloned after formData() declared content-length: 63 and sent 125 bytes. Depending on the receiver, that request is rejected, truncated, or left waiting for bytes that never arrive.
Are you affected?
You are affected on Hono 4.12.31 to 4.13.3 if you call cloneRawRequest() on a multipart request after reading it with formData() (directly, through parseBody(), or through a form validator) and forward the clone.
Fix or workaround
Upgrade to Hono 4.13.4 or later. On an affected version, delete the header before forwarding the clone so the runtime computes it. We verified this on 4.13.3:
const clone = await cloneRawRequest(c.req);
clone.headers.delete('content-length');
// forward the clone as beforeWhat happened next
Opened
#5133 opened
Merged
Release
Hono 4.12.31 ships the change
Warpway replay
Warpway replay: given the pull request as it stood before merge, two medium-severity findings
Frequently asked questions
Which Hono versions send a stale Content-Length?
Hono 4.12.31 through 4.13.3, when cloneRawRequest() clones a multipart request whose body was already read with formData() and the original request carried a Content-Length header. 4.13.4 drops the header.
What goes wrong downstream?
The re-encoded body has a new multipart boundary and usually a different length. In our test on 4.13.3 the clone declared 63 bytes and sent 125. Servers and proxies that trust Content-Length can reject the request, truncate the body or wait for bytes that never come.
How do I work around it on an affected version?
Delete the header from the clone before forwarding it: clone.headers.delete('content-length'). The runtime then sets the correct length. We verified this on 4.13.3.
How we ran this
- 01
Replay the pull request as it stood before merge
Its final code, title, description and commits, and the repository at the fork point. No review comments, no bug reports, nothing after merge.
- 02
Review with Warpway’s default lenses
The production role models, lenses and default policy, run through Codex subscription access. One run per pull request, with no repository configuration.
- 03
Compare with what actually happened
Later fixes, reverts and bug reports in the public history, linked from every page so you can check our reading.
This pull request is one of 12 we replayed on 2026-10-07 from hono, axios and requests, including ones that were never fixed or reverted and the cases Warpway got wrong. Warpway was not installed on these repositories at the time; every review here is a replay. Older pull requests may appear in the model's training data, which is one reason we include recent ones and a bug nobody had reported. See every result from the pilot.
These replays used Codex subscription access rather than the production OpenAI API. They establish the recorded findings, not production API cost, general accuracy, customer time savings or an approval rate. The projects are open-source examples, not Warpway customers.
More case studies
- psf/requests #6667Python
requests 2.32 SSLContext regression: the wrong mTLS client certificate
- Warpway flags in a replay
- Client certificates leaking across requests through one shared SSLContext
- What happened
- Reported by users from 7 days after merge; reverted a year later in 2.32.5
- honojs/hono #5366TypeScript
Hono formData() after text() silently corrupts binary file uploads
- Warpway flags in a replay
- Binary file uploads silently altered when text() is read first
- What happened
- Not previously reported; we filed it as honojs/hono#5539
- honojs/hono #4707TypeScript
Hono 4.12.0: c.json(undefined) throws "Value is not JSON serializable"
- Warpway flags in a replay
- c.json(undefined) throws on the new fast path instead of returning a response
- What happened
- Reported 4 days after merge; fast path reverted in 4.12.2