A Hono Form Parsing Regression Inside a Small Fix
A targeted replay found one real form parsing regression among eight findings in Hono v4.12.28.
Regression Case File | Open-source historical replay
Hono v4.12.28 shipped a change intended to make Content-Type matching case-insensitive. Inside that change, a form-body path changed too. If middleware called c.req.formData() and a later handler called c.req.parseBody() on the same request, multipart forms could fail and urlencoded forms could quietly return corrupted fields.
We selected that known regression window and ran a targeted historical replay of v4.12.27 to v4.12.28. Early returned eight findings. One was classified as a Regression, with a verdict score of 8 out of 10. It described this defect and its mechanism.
This was not a blind trial. Independent historical replay. Not affiliated with Hono.
The Failure
The public Hono issue #5129 reported the failure 11 days after v4.12.28 shipped. A route could read formData() in middleware, then call parseBody() later in the same request and receive a wrong result.
The outcome depended on the request encoding:
multipart/form-datacould throwFailed to parse body as FormData, producing an HTTP 500.application/x-www-form-urlencodedcould return HTTP 200 while replacing the expected fields with multipart boundary text.
The second outcome matters most. The request appears successful, but its form values are wrong.
Release Timeline
The change merged on July 1, 2026 and shipped in v4.12.28 on July 6. A user reported the defect on July 17. Hono maintainers fixed it the next day in v4.12.31.
The released failure window covered three versions, v4.12.28 through v4.12.30. The reproduction passes in v4.12.26 and v4.12.27, fails in those three affected versions, then passes again in v4.12.31.
The measured release boundary was clean before v4.12.28 and after v4.12.31.
How Parsing Broke
Before v4.12.28, parseFormData() read request.formData(). When middleware had already read the form, Hono returned that cached formData value directly.
The new version first read request.arrayBuffer(). If formData was already cached but arrayBuffer was not, Hono derived an array buffer from the cached form with new Response(cachedFormData).arrayBuffer().
That conversion serializes the form with a new multipart boundary. The next parser step still uses the original request Content-Type header, which names the old boundary. The bytes and header no longer agree.
The failure is a cross-cache boundary mismatch, not an attempt to re-read an empty request stream.
The change came from PR #5067. Its stated purpose was Content Type normalization. The release window contained 10 commits and 16 changed files. This mechanism sat inside a change that otherwise corrected case-sensitive media-type matching.
The Historical Replay
This case was selected because the regression was already known. Early did not receive the expected answer. It analyzed the source and destination tags, v4.12.27 and v4.12.28, then returned eight findings from that release window.
The output is useful because it gives a denominator. Early classified one finding as a Regression and seven as Expected changes. We independently verified the one Regression finding against the release code, the reproduction matrix, the public report, and the later upstream fix.
We did not independently verify every Expected classification. The precision claim is intentionally narrow: the single Regression-class finding was a true positive.
What Early Found
The finding was titled “parseFormData corrupts body via cross-key cache derivation.” It identified the specific caller order, the formData to arrayBuffer conversion, the new multipart boundary, and the mismatch with the original header.
| Verdict | Count | Review result |
|---|---|---|
| Regression | 1 | The Hono form parsing defect |
| Expected | 7 | Not independently verified for this case |
Condensed evidence panel based on the historical replay output. The later upstream issue and fix were used only during human verification.
The explanation was more precise than the researchers’ own pre-run notes. Those notes initially described a consumed stream. The replay correctly identified the cross-key cache derivation and boundary mismatch instead. Human verification corrected the research record to match the finding, not the reverse.
The later PR #5131 added a formData cache short-circuit to parseBody(), addressing the same formData()-before-parseBody() path. A companion PR #5133 hardened related multipart boundary handling. Those changes are later independent confirmation, not analysis input.
Verification and Limits
The culprit passed the project’s automated checks. Twenty check runs passed and two were skipped, with no failures. Codecov reported patch coverage of 76.47 percent against an 80 percent target without blocking the merge.
Hono’s checks spanned Node, Bun, Deno, workerd, AWS Lambda, Lambda@Edge, Fastly Compute, coverage, JSR validity, types, and bundle size. This case does not establish that its tests were generally weak. It shows that a specific cross-helper form-body sequence was not represented by the checks that ran on this change.
The replay happened after the incident, and the window was deliberately selected because it contained a known regression. It is a case study, not a claim about detection rates across arbitrary releases. It does not show that Early prevented this release or that every regression will be found.
The useful conclusion is narrower. In a targeted replay, Early identified a real form parsing regression inside a release change that looked like a small compatibility fix. Its verification procedure failed on the affected releases and passed before and after them.
Reproduce the Failure and Verify the Finding
The replay materials, including the release history, Early-generated investigation prompt, and offline reproduction, are available by request. Contact us to request access.
Once you have access to the replay materials, you can inspect the evidence yourself:
- Clone the repository and inspect the code and Git history between v4.12.27 and v4.12.28.
- Start Claude Code or Codex in the repository, give it the supplied Early-generated prompt, and require file, line, commit, and tag evidence as it verifies, refutes, or corrects the finding.
- Run the npm-artifact reproduction across v4.12.26 through v4.12.31. It confirms the clean, clean, fail, fail, fail, clean release sequence without a project build or upstream test suite.
The prompt supports an independent audit of the finding. The script reproduces the runtime failure. Neither exercise reruns the original Regression Guard analysis.
Contact us for the technical replay guide, which includes prerequisites, exact commands, expected results, and primary-source links.
Sources and Method
Primary public sources include the user report, the introducing release comparison, the introducing pull request, and the later upstream fix.
Human verification compared the historical product output with the release code, an offline npm-artifact reproduction, and the later public upstream record. The replay did not participate in the original Hono release.