Skip to content

A usage limit whose reset has passed does not fail the run when another failed node has a later deadline #4412

Closed

Description

@Wirasm

Problem

When a pass fails, usageLimitWait in packages/workflows/src/dag-executor.ts:12361-12403 decides whether the run waits for a provider limit to reset or fails. It checks reset_passed only against the latest deadline across all failed nodes (dag-executor.ts:12391: if (latest.resumeAt <= now) return skip(latest.nodeId, 'reset_passed')). The loop at dag-executor.ts:12380-12388 keeps only the latest resumeAt and never checks the others against now.

So if node A failed with quota_exhausted and a reported reset that has already passed, and node B in the same pass failed with a limit whose deadline is still ahead, the run pauses and waits for B's deadline instead of failing.

Two places say the run should fail in that case:

  • packages/docs-web/src/content/docs/reference/configuration.md (the usage-limit section under the workflows settings table): "The run waits only when every node that failed in that pass hit a usage limit with a reported reset that is still ahead, or a rate limit ... Any other failure, a usage limit without a reset time, or a usage limit whose reset has already passed fails the run as before."
  • The doc comment on usageLimitWait itself (dag-executor.ts:12351-12360): "when every failed node either hit a provider usage limit whose reset the provider reported and that is still ahead, or hit a rate limit ..."

The same latest-only ranking appears in aggregateProviderFailure (packages/workflows/src/executor-shared.ts, right after providerLimitResumeAt at :90-96). It reports the latest providerLimitResumeAt among an aggregate's limits, so a loop group or fan-out child whose usage-limit reset has passed is hidden behind a sibling with a later deadline before usageLimitWait sees it.

The only test is single-node: dag-executor.test.ts:17774-17800 ('the reported reset has already passed'). No test covers a passed reset next to a later deadline.

Why it matters

A reset time in the past means the provider's limit signal can't be trusted for this node. The documented contract is that Archon fails instead of guessing. Today the code and its own doc comment disagree, and the user docs describe behavior the engine does not have. Two sites that should agree and don't are a defect under AGENTS.md.

Why now

PR #4338 just extended this wait to rate limits and touched the same function. The contract is fresh and the code is small.

Desired outcome

The engine matches its documented contract: any failed node in the pass (including a descendant reported through an aggregate) whose usage limit has a reported reset that has already passed makes the run fail, with a quota_resume_skipped event (reason: 'reset_passed') naming that node. If the implementer finds a concrete reason the current wait-for-the-latest behavior is the right one, change the docs and the doc comment instead, and say why in the PR. Do not leave the code and the docs disagreeing.

Invariants

  • Only the typed provider failure class decides. Never read error text.
  • A pass whose failed nodes all carry limits with deadlines still ahead still waits for the latest one.
  • Rate limits keep their computed backoff when no reset is ahead. A rate limit never counts as reset_passed.
  • reset_unavailable, disabled and container_unsupported behavior is unchanged.

Acceptance

  • A test with two failed nodes, one quota_exhausted with a past reset and one with a future deadline, shows the run fails with reset_passed on the first node (or, if the docs change instead, shows the documented behavior).
  • A test covers the same case inside an aggregate (loop group or fan-out), or the PR explains why the aggregate path cannot reach it.
  • configuration.md, the usageLimitWait doc comment and the aggregateProviderFailure doc comment all describe what the code does.

Found by Archon run 26bdc2cb-a312-4b0e-90dc-852419ce19fc and verified at c47d37a by the archon-discoveries workflow.

Current scope (maintainer ruling on the build queue's question on a stopped run, 2026-10-20)

Retain latest-deadline waiting when an expired quota reset coexists with a future provider-limit deadline, including inside aggregates. Use the issue's documentation alternative, explain the preserved automatic continuation behavior, and satisfy its tests, documentation alignment and unchanged-behavior invariants.

The maintainer's words: Keep the wait. The run already has to wait for the other node's later deadline, and when it resumes the node with the expired reset retries too, so failing early gains nothing. Fix the configuration reference to describe latest-deadline waiting. Keep the change to the docs plus at most one behaviour test if one is missing; the existing aggregateProviderFailure test already covers the behaviour.

Activity

  1. added
    archon-planTriage: ready; a plan comes before delivery
    archon-size-mTriage: several files or two surfaces
    archon-type-bugTriage: something that worked is broken
    archon-triageRequest: triage this item (a listed maintainer adds it to promote an outside item)
    and removed
    archon-triageRequest: triage this item (a listed maintainer adds it to promote an outside item)
    on Oct 19, 2026
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

    archon-planTriage: ready; a plan comes before deliveryarchon-size-mTriage: several files or two surfacesarchon-type-bugTriage: something that worked is brokenarea: docsDocumentationarea: workflowsWorkflow engine

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      Sponsor
      SponsoredKunjungi sekarang
      Promo