Visitar URL original
Fix overflow when decoding durations with large day counts by NgoQuocViet2001 · Pull Request #1320 · msgspec/msgspec · GitHub
Skip to content

Fix overflow when decoding durations with large day counts - #1320

Merged
Siyet merged 3 commits into
msgspec:mainfrom
NgoQuocViet2001:fix-timedelta-day-overflow
Nov 11, 2026
Merged

Siyet merged 3 commits into
msgspec:mainfrom
NgoQuocViet2001:fix-timedelta-day-overflow

Conversation

@NgoQuocViet2001

Copy link
Copy Markdown
Contributor

Decoding an ISO 8601 duration caps the integer part of each segment at 247. That is enough for hours, minutes and seconds but not for days, since 86400 * 247 does not fit in int64. Day counts from 106751991167301 up to 2**47 wrap around, pass the range check, and then either decode to an unrelated value or raise a bare OverflowError:

>>> msgspec.json.decode(b'"P106751991167301D"', type=datetime.timedelta)
datetime.timedelta(days=-579025221, seconds=61184)
>>> msgspec.json.decode(b'"P140737488355328D"', type=datetime.timedelta)
OverflowError: days=-1158050442; must have magnitude <= 999999999

Smaller out-of-range values like "P1000000000D" already raise ValidationError: Duration is out of range, as the parser's doc comment and test_decode_timedelta_out_of_range expect. This PR caps the integer part at MS_TIMEDELTA_MAX_SECONDS instead. Anything larger is out of range for every unit, and 86400 * x always fits in int64 under the new cap, so the existing range check rejects these inputs too. JSON, MessagePack and convert share this parser.

  • Ran: pytest tests/unit/test_common.py -k TestTimeDelta. The 4 new cases fail on main for json and msgpack (8 failures, DID NOT RAISE or OverflowError); with the fix, 200 passed.
  • Ran: pytest tests/unit: 6591 passed, 157 skipped.
  • Ran: ruff check, ruff format --diff, codespell: clean.
  • Checked: boundary values such as P999999999DT86399.999999S and PT86399999999999S decode the same as before. About 200k random durations either match timedelta(seconds=...) or raise ValidationError.

The integer part of each duration segment was capped at 2**47, which is
enough for hours, minutes and seconds but not for days: 86400 * 2**47
does not fit in int64. Day counts between about 1.07e14 and 2**47
wrapped to a negative value, slipped past the range check and then
either decoded to an unrelated timedelta or raised OverflowError.

Cap the integer part at the maximum timedelta seconds instead. Larger
values are out of range for any unit, and the product with the day
scale now always fits in int64, so the existing range check catches
these inputs and raises ValidationError.

Siyet commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Could you merge the latest main into this branch? It includes the pypa/gh-action-pypi-publish update for workflow lint, which is still missing here.

@NgoQuocViet2001

Copy link
Copy Markdown
Contributor Author

Done, merged the latest main in 2b73df8.

@Siyet
Siyet merged commit 8d7d7d7 into msgspec:main Nov 11, 2026
21 checks passed

Siyet commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Thx for contributing the fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants