Skip to content

workflow: a Stop that cannot release its Bot freezes the run and its lane slot forever #2452

Closed

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Dev-3
Claimed-by: FlyBot Lead
Claim-Id: 01e37876ef3a4ab49748c04ae6b52229
Branch: dev/3-workflow-stop-retry
Base: 16d1645

plugins/workflow: a Store/Reset whose Bot release fails freezes the run (paused + releasePending, #1476) and no longer has any way out on its own — the frozen run holds a maxConcurrent slot until the owner presses Stop again.

Steps to reproduce

  1. A workflow with two nodes; run a task so one node is in flight and one run Bot exists.
  2. Make the host refuse to remove that Bot (in the test this is one release hook that throws).
  3. Press Stop on that run. reset({ runId }) releases what it can, keeps the failed id, and calls hold(): run.paused = true, run.releasePending = true.
  4. Do nothing else — do not press Stop again.

Expected: the run's step deadline (45 minutes by default) is the bounded wait the subsystem doc promises, so the lane gets its slot back: its Bot is released again and the run ends, reading paused-expired in the Runs tab.

Actual: the lane stays at its maxConcurrent limit forever. hold() writes those two flags without calling syncTimers(), so the deadline of the node the run was on is still armed; when it fires, expireStep() returns at if (… || run.paused === true) return — the timer is already deleted at that point, nothing re-arms it, and nothing retries the release. docs/subsystems/workflow.md:41 states the contract this breaks: "a step still unanswered after its workflow's stepTimeoutMinutes (45 minutes by default) fails the run and releases its Bots (#1530, #1580)" — one hold() and the workflow can queue tasks that never start.

Scope

plugins/workflow/src/service.js: the deadline of a run frozen by a failed Stop retries the release; when it succeeds the run is ended with failure.code: 'paused-expired' and archived (slot freed, lane.history row, one stage line), and when it still fails the deadline is armed again with the id that would not go still named. No change to resume(), to releasePending's meaning, or to what the panel draws; an owner's own Pause is untouched (its deadline is cleared as before, so it is still never timed out). One test file, plugins/workflow/test/stop-retry-deadline.test.js and one changelog.d fragment; docs/subsystems/workflow.md is unchanged because it already promises this contract, so the branch carries a Docs-not-needed: line.

Duplicate check

No existing issue covers this: #1476 introduced paused/releasePending and the Keep-the-Bot rule (it is about the flags, not about the timer), #1530/#1580 introduced the step deadline and its bounded hand-off (their tests cover an unanswered step and an owner Pause, and deadline.test.js pins "a paused run holds no timer" for the owner's Pause — the failed-Stop run is the case neither covers), and #2426/#2423 are the label fix on the same frozen run (Pause/Running drawn instead of Paused), a different symptom in plugins/workflow/client.js.

Activity

  1. added a commit that references this issue on Nov 4, 2026
    231190b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions