The Tag and create GitHub Release step of .github/workflows/release.yml fails with HTTP 422: Validation Failed — body is too long (maximum is 125000 characters) when the CHANGELOG section extracted into RELEASE_NOTES.md exceeds GitHub's release-body limit. It happened on v0.1.10: the section is 148,703 characters (the #1213 burndown's 44-issue notes), and the step died after PyPI had already published the immutable archives and after the tag was created — leaving the release half-cut at the worst possible point in the pipeline.
What the recovery looked like (v0.1.10, 2026-08-12)
Manual, mirroring the step exactly: the run's built artifacts downloaded and hash-verified three ways (SHA256SUMS = local = PyPI digests), then gh release create v0.1.10 --verify-tag --title v0.1.10 --latest with the wheel, sdist, and SHA256SUMS, and a generated body — the section's bold bullet lead-ins extracted mechanically as a headline index, plus a link to the canonical section at the tag (CHANGELOG.md#0110---2026-08-12), 9,228 characters.
Fix direction
Make the step total: before gh release create, if RELEASE_NOTES.md exceeds a safe threshold (say 120,000 bytes), regenerate it as the recovery shape — the ### subsection headers, each bullet's - **lead-in** line, and the canonical CHANGELOG link at the tag — so the release always carries a body that fits and the full notes remain in the CHANGELOG, which is already the release notes of record. A release this large is rare, but the failure lands after the immutable PyPI publish, which is exactly where the workflow must not fail.
The
Tag and create GitHub Releasestep of.github/workflows/release.ymlfails withHTTP 422: Validation Failed — body is too long (maximum is 125000 characters)when the CHANGELOG section extracted intoRELEASE_NOTES.mdexceeds GitHub's release-body limit. It happened on v0.1.10: the section is 148,703 characters (the #1213 burndown's 44-issue notes), and the step died after PyPI had already published the immutable archives and after the tag was created — leaving the release half-cut at the worst possible point in the pipeline.What the recovery looked like (v0.1.10, 2026-08-12)
Manual, mirroring the step exactly: the run's built artifacts downloaded and hash-verified three ways (SHA256SUMS = local = PyPI digests), then
gh release create v0.1.10 --verify-tag --title v0.1.10 --latestwith the wheel, sdist, and SHA256SUMS, and a generated body — the section's bold bullet lead-ins extracted mechanically as a headline index, plus a link to the canonical section at the tag (CHANGELOG.md#0110---2026-08-12), 9,228 characters.Fix direction
Make the step total: before
gh release create, ifRELEASE_NOTES.mdexceeds a safe threshold (say 120,000 bytes), regenerate it as the recovery shape — the###subsection headers, each bullet's- **lead-in**line, and the canonical CHANGELOG link at the tag — so the release always carries a body that fits and the full notes remain in the CHANGELOG, which is already the release notes of record. A release this large is rare, but the failure lands after the immutable PyPI publish, which is exactly where the workflow must not fail.