Fix AirbyteJobSensor marking a cancelled job as success in deferrable mode - #69786
Conversation
… mode When the deferred sensor resumes, execute_complete only failed on an "error" event. A job cancelled while the sensor was deferred yields a "cancelled" event, which fell through to the success path, so the task was marked SUCCESS and downstream tasks ran against a sync that never completed. The same sensor fails correctly on a cancelled job in non-deferrable mode, and the sibling AirbyteTriggerSyncOperator already fails on the cancelled event, so deferrable sensor runs silently diverged from both. Fail closed on any non-success status so unrecognized statuses cannot be treated as success either.
|
Confirmed the gap: |
jroachgolf84
left a comment
There was a problem hiding this comment.
LGTM, tests look good.
SameerMesiah97
left a comment
There was a problem hiding this comment.
I think the state machine can be more explicit. Please see my comment below.
|
Both good points and addressed in 02ac81f as it now makes the states explicit ( |
potiuk
left a comment
There was a problem hiding this comment.
Thanks — this is a data-correctness bug, not a cosmetic one, and worth spelling out.
execute_complete raised only on status == "error". Every other status fell through to the success path — including "cancelled". So a deferred AirbyteJobSensor whose sync was cancelled logged "completed successfully", returned None, and the task went green. Downstream tasks then ran against incomplete data with nothing anywhere indicating a problem. Silent success on a failed job is about the worst failure mode a sensor can have, and it diverged from poke(), which handled cancellation correctly — so the same sensor behaved differently depending on deferrable.
The fix is right, and the fail-closed default is the part that matters most. Handling success/error/cancelled explicitly fixes today's bug; raising on any unmodelled status fixes tomorrow's, when Airbyte adds a state this sensor has never heard of. Inverting the default from "assume success" to "assume failure" is the correct posture for a sensor.
The tests match: parametrising over error, cancelled and unmapped_status covers all three failure routes, and asserting None on success confirms the happy path wasn't broken in the process. Decrementing the ratchet 6 -> 5 rather than removing the line is also correct, since the other five raises remain.
One small improvement, not blocking: the fail-closed branch raises RuntimeError(event["message"]), so for an unrecognised status the operator sees only whatever Airbyte put in message — the status itself appears just in a log.debug. Including it in the exception (f"Unexpected job status {status!r}: {event['message']}") would make the one case you can't anticipate the easiest to diagnose.
Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
Problem
AirbyteJobSensorin deferrable mode resumes viaexecute_complete, which only fails on anerrorevent.AirbyteSyncTriggeryield acancelledevent, which fell through to the success path: the task is marked SUCCESS and downstream tasks run against a sync that never completed.poke()raises, the deferrableexecute()raises when it seesCANCELLEDbefore deferring, and the siblingAirbyteTriggerSyncOperator.execute_completealready fails on thecancelledevent (added in Implement execution_timeout semantics for AirbyteTriggerSyncOperator in deferrable mode #64051, which did not cover the sensor).Change
execute_complete, matchingpoke(). Thecancelledevent now fails the task, and unrecognized statuses fail closed instead of being treated as success.Live verification
Ran a real
airflow standalone(scheduler, triggerer, API server) against a live local endpoint serving the Airbyte jobs API, with the connection host pointed at it. Both runs: sensor deferred while the job wasrunning, then the job was flipped tocancelled.RuntimeError: Job run 1 has been cancelled., run marked Failed.Tests
test_execute_complete_fails_when_job_did_not_succeed(parametrized:cancelled, unrecognized status) fails on the code before this change; the failure output shows the bug verbatim ("completed successfully." on a cancelled event).test_execute_complete_succeeds_on_success_eventguards the happy path.Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Fable 5) following the guidelines