Repository navigation
feat: Consistently Handle FatalDecisionError - #200
Conversation
f637835 to
0ab747f
Compare
0ab747f to
442fe72
Compare
442fe72 to
52ac5aa
Compare
| The DeterministicEventLoop stops running workflow code and re-raises error, | ||
| so the decision task fails even if workflow code catches it. | ||
| """ | ||
| loop = events._get_running_loop() |
There was a problem hiding this comment.
nit: use public API asyncio.get_running_loop but need to handle RuntimeError if not found. In this case, maybe instead raise FatalDecision("bug: run fail_decision not in deterministic eventloop") from
There was a problem hiding this comment.
We use this API elsewhere, it's apparently intended to be used by event loop implementations:
https://github.com/python/cpython/blob/main/Lib/asyncio/events.py#L711 .
| if self._decision_failure is None: | ||
| self._decision_failure = error | ||
|
|
||
| def abort_if_decision_failed(self) -> None: |
There was a problem hiding this comment.
Do we really need this level of fail-fast? User really has to catch BaseException and have high CPU workflow code to break the SDK.
It does make the code a little too complicated. But I'm open to keep it but needs some documentation
52ac5aa to
5cc05da
Compare
4e49d93 to
23a1021
Compare
Workflow code can catch FatalDecisionError and often does so accidentally via `except Exception`. Consistently fail the decision: - Store the FatalDecisionError on DeterministicEventLoop. Reraise it to the WorkflowEngine for reporting. - Change FatalDecisionError to extend BaseException rather than Exception. This is a more correct base class that users are unlikely to accidentally catch. We want to collect and report only the first FatalDecisionError as Workflow code might incidentally cause additional errors once that has happened and they're likely red herrings.
23a1021 to
53867c9
Compare
Code Review ✅ Approved 6 closed / 6 findings🟡 Medium risk · Making FatalDecisionError bypass workflow catches and persistently fail decision tasks could halt affected workflows instead of letting them continue. Adds consistent handling of FatalDecisionError so workflow code cannot swallow it, with the event loop storing the first fatal error and reraising it to the WorkflowEngine, and the engine destroyed after each decision task. Resolved this session: destroy() now runs task coroutines in the task's contextvars Context, tasks created during destroy() are unwound, the DecisionManager test loop issue and the create_task(eager_start=False) TypeError were fixed, the predicate test now reaches the waiter-poll abort in _run_once, and the engine destroy/finally handling promised in the PR description is now implemented. No open issues remain. ✅ 6 closed✅ Bug: destroy() runs task coroutines outside the task's contextvars Context
✅ Edge Case: Tasks created by finally blocks during destroy() are never unwound
✅ Cross-PR: #199's DecisionManager tests hit AttributeError on a plain asyncio loop
✅ Edge Case: create_task(eager_start=False) now fails with TypeError
✅ Quality: Predicate test never reaches the new waiter-poll abort in _run_once
...and 1 more closed from earlier reviews Verification Repos 2 inspected · PRs 3 inspectedFunctional validation No results Rules No rules evaluated Broader impact Repos 2 inspected PRs 3 inspectedCross-repo 2 repositories inspected 0 open impacts
Cross-PR 3 PRs inspected 0 open impacts
For more verification details, comment OptionsAuto-apply is off → Gitar will not commit updates to this branch. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Gitar |
Workflow code can catch FatalDecisionError and often does so accidentally via
except Exception. Consistently fail the decision:We want to collect and report only the first FatalDecisionError as Workflow code might incidentally cause additional errors once that has happened and they're likely red herrings.
What changed?
Why?
How did you test it?
Potential risks
Release notes
Documentation Changes