fix(sdks/python): handle empty CREATE payload in durable waits#4444
Open
STiFLeR7 wants to merge 1 commit into
Open
fix(sdks/python): handle empty CREATE payload in durable waits#4444STiFLeR7 wants to merge 1 commit into
STiFLeR7 wants to merge 1 commit into
Conversation
aio_sleep_for and aio_wait_for_event both assumed the engine's wait-for
result always contains at least one match, calling next(iter(matches))
and indexing into the match list unconditionally. An empty CREATE
payload (a legitimate response - aio_wait_for already falls back to {}
for an empty proto payload) raised StopIteration inside a coroutine,
which PEP 479 turns into RuntimeError: coroutine raised StopIteration.
Factor the shared extraction logic into _first_wait_match_or_none,
which treats a missing/empty CREATE dict or an empty match list as
"no match" instead of raising, and use it at both call sites.
Fixes hatchet-dev#4349
|
@STiFLeR7 is attempting to deploy a commit to the Hatchet Team on Vercel. A member of the Team first needs to authorize it. |
mrkaye97
reviewed
Jul 16, 2026
| ## the list of matches will only have one item, so we can extract and parse it. | ||
| ## An empty payload is a legitimate response (see `_first_wait_match_or_none`), | ||
| ## so fall back to an empty dict rather than raising. | ||
| sleep = _first_wait_match_or_none(res) or {} |
Contributor
There was a problem hiding this comment.
needing to fall back here with or {} feels a bit odd - can we have _first_wait_match_or_none return only dict[str, Any] instead, and return {} in the None cases?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #4349 —
aio_sleep_forandaio_wait_for_eventonDurableContextcrash withRuntimeError: coroutine raised StopIteration(PEP 479) when a durable wait completes with an emptyCREATEpayload, instead of falling back gracefully.Root cause
Both methods extracted the first match via:
next(iter(matches.items()))raisesStopIterationwhenmatchesis empty (e.g.CREATEmissing,{}, or a key mapping to an empty list) — and since this executes inside a coroutine, Python 3.7+'s PEP 479 turns thatStopIterationinto aRuntimeErrorinstead of letting it propagate as a normal exception.aio_wait_foralready treats an empty proto payload as valid and falls back to{}, so a durable wait genuinely can complete with no payload — this wasn't an edge case, it was a reachable path that just crashed instead of degrading gracefully.Fix
Added a shared helper,
_first_wait_match_or_none, used by bothaio_sleep_forandaio_wait_for_event, that returnsNonefor any empty-payload shape (missingCREATE, empty dict, or empty match list) instead of raising. Both call sites now do_first_wait_match_or_none(res) or {}.Testing
tests/unit/test_durable_wait_empty_payload.py: parametrized tests for_first_wait_match_or_nonecovering empty/missing payload shapes and normal match extraction, plus async tests constructingDurableContextwith a mockedaio_wait_forto verifyaio_sleep_for/aio_wait_for_eventno longer crash and still correctly extract a payload when one is present.poetry run python -m pytest tests/unit/test_durable_wait_empty_payload.py— 7 passed.poetry run ruff check/poetry run ruff format --check— clean.poetry run mypy— no issues.upstream/main; confirmed no upstream commits touchedcontext.pysince this branch was created, so no merge risk.Pre-existing, unrelated: 6 tests in
test_shutdown_ordering.pyfail locally on Windows (loop.add_signal_handlerraisesNotImplementedError—SIGINThandler registration isn't supported by Windows' asyncio event loop). Verified viagit stashthat this reproduces identically on unmodifiedupstream/main, so it's a pre-existing platform limitation, not a regression from this change.Disclosure
Prepared with AI assistance (Claude Code). I read the surrounding
Context/DurableContextcode and the PEP 479 mechanics before making the change, wrote the fix and its regression tests, and verified ruff/mypy/pytest all pass locally, including confirming the pre-existing Windows-only test failures are unrelated and reproduce on a cleanupstream/maincheckout.