Repository navigation
Carry a provider failure out of a container body - #2131
Merged
Merged
Conversation
ppXD
force-pushed
the
fix/carry-a-provider-failure-out-of-a-loop-body
branch
from
October 19, 2026 03:04
12b3b4f to
a37d76b
Compare
ppXD
force-pushed
the
fix/let-a-reauthorization-win-over-the-repair-sweep
branch
from
October 19, 2026 03:05
90c3126 to
2dcd2be
Compare
ppXD
changed the base branch from
fix/let-a-reauthorization-win-over-the-repair-sweep
to
main
October 19, 2026 03:06
A provider failure inside a flow.map branch or a flow.loop pass that ended the container was written on the body node's own node.failed record and nowhere the run's remedy could find it. The run's failure is the one the node that ENDED the run recorded; for such a run that node is the container, and the container's node.failed carried only its sentence, so the run page said "Node 'map' failed." with no card, no credential and no repair. The container's record now carries the failure of the cell whose unhandled failure ended it: that node's own recorded failure, else the agent run that last worked in that very cell, read through a new ICellFailureReader in the order and with the parsers RunFailureReader uses for the node that ended a run. A nested container reads the record the level below wrote, so the failure travels as far as the nesting goes and the run's remedy is the one a top-level node of the same failure gets. Fail-fast and collect policies, retries, partial results and the sentences containers write are untouched; only what the record carries changes. The cell is chosen deterministically. A terminate map whose branches fail concurrently answers for the first failing branch by element order, which is the branch its sentence already names, never whichever finished first. A branch replayed from the ledger while a sibling was parked is still the cell that ended the map and is read back from its own record. Nothing else is read: a provider failure the body handled with an error edge, one a try caught, one a sibling branch met, or one a continue policy skipped leaves nothing on a container that later fails for another reason, and a container that fails on its own (a tripped budget, a binding that is not an array) carries none. A map-branch rerun replays a failed sibling into its fork, and the agent run that worked in that branch belongs to the original run, so the fork could never find it: its map failed again on the replayed branch and answered with no remedy. The rerun seeder now re-emits a failed cell with the failure the original run recorded for it (the cell's own record, else its agent run, through the same reader), so the fork's map answers for the replayed branch as the original's did. A failed cell that recorded no failure is re-emitted exactly as before. flow.iterate is a single-expression node, not a container, and flow.subworkflow runs a child run: a failure inside a child run is not carried to the parent node by this change. Additive: a container record gains the failure key a node record already carries, no migration, no API or wire change. A row written before this reads exactly as it did, and a reader that predates the key ignores it.
ppXD
force-pushed
the
fix/carry-a-provider-failure-out-of-a-loop-body
branch
from
October 19, 2026 03:06
a37d76b to
85357b9
Compare
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
flow.mapbranch or aflow.looppass that ended the container was written only on the body node's record, so the run page said "Node 'map' failed." with no credential, repository or repair. The container'snode.failedrecord now also carries the failure of the cell that ended it, which is that node's own recorded failure, else its last agent run's. It is read through a newICellFailureReaderin the same order and with the same parsersRunFailureReaderuses, and a nested container reads the record the level below wrote.RunFailureReadertherefore returns the same remedy as for a top-level node.terminatemap whose branches fail together answers for the first failing branch by element order, which is the branch its sentence already names, never whichever finished first. A branch replayed from the ledger while a sibling was parked is still the cell that ended the map. Failures the body handled, aflow.trycaught, a sibling met or acontinuepolicy skipped leave nothing on a container that later fails for another reason, and a container that fails on its own (a tripped budget, a non-array binding) carries none.flow.iterateis a single-expression node, not a container, andflow.subworkflowruns a child run. A failure inside a child run is not carried to the parent node by this change.failurekey a node record already carries. Rows written before read exactly as they did, a reader that predates the key ignores it, and a rollback is a plain revert.Test plan
GitLabDoorkeeperStub: map, loop, loop-in-map and map-in-loop each end on a dead credential and the run detail remedy names the credential and repository, with every container's sentence untouched.flow.try-caught failure followed by a non-provider container failure answers no stale remedy (flow.iterateand a flaky node as the non-provider failure).CellFailureReaderboundary (newest record, then last agent run; nothing from another iteration, node, run, team, or another run of the same team); the real rerun seeder carries a failed cell's failure into the fork for a kept top-level cell and a sibling branch, from the cell's own record and from the original run's agent run, and leaves a failure-less cell's record unchanged.RerunMapBranchAsyncwhose fork fails on the replayed failed branch answers with the original run's remedy, never the fresh branch's.POST /api/workflows/runs/{id}/rerun-map-branch; a run recorded before containers carried the failure answers exactly as it did.flow.subworkflowparent node, if wanted.