Skip to content

Agent launcher: rate-limit awareness and backoff for Copilot API 429s #227

Description

@jafreck

Problem

During a large zstd C→Rust migration (310 tasks, maxParallelAgents: 12), the Copilot API started returning HTTP 429 rate-limit errors ~50 minutes into Phase 4. This caused a cascading failure:

  • 68 agent invocations failed to emit aamf-json output blocks because the Copilot CLI exited with code 1 after receiving a session.error with errorType: "rate_limit".
  • Failed invocations burned retry budget — tasks that had genuine parity issues couldn't get enough clean attempts to converge.
  • The run hit terminal exhaustion on task-157-0 after 7 retries (mix of rate-limited and real failures), halting the entire migration.

Breakdown by agent:

Agent Missing aamf-json
parity-failure-resolver 45
parity-verifier 15
code-migrator 5
test-writer 3

The rate limit error from the Copilot CLI looks like:

{
  "type": "session.error",
  "data": {
    "errorType": "rate_limit",
    "message": "Sorry, you've hit a rate limit... Please try again in 2 hours.",
    "statusCode": 429
  }
}

Current behavior

  • Rate-limited invocations are treated identically to other failures — they consume retry attempts and are retried immediately.
  • maxParallelAgents controls the code-migrator branch concurrency, but each branch also spawns 2 sub-agents (parity-verifier + test-writer) via ParallelExecutor(2), so effective concurrent sessions can be up to maxParallelAgents * 3.
  • No pause, backoff, or global throttle is applied when rate limits are detected.

Proposed behavior

  1. Detect rate limits in agent output. In agent-launcher.ts, parse the agent's event log for session.error with errorType: "rate_limit" or check for exit code 1 with no aamf-json output and rate-limit markers in stderr/stdout.

  2. Classify rate-limited invocations as transient infrastructure errors, not agent failures. They should not consume the task's maxRetriesPerTask budget.

  3. Global backoff on rate limit detection. When any agent invocation hits a rate limit:

    • Pause all new agent launches for a configurable backoff period (e.g., start at 60s, exponential up to the retry-after value from the error if available).
    • Log a clear warning: "Rate limit detected — pausing all agent launches for {duration}s".
    • Resume launches after the backoff expires.
  4. Expose a rateLimitBackoff config option (optional):

    {
      "options": {
        "rateLimitBackoff": {
          "initialDelayMs": 60000,
          "maxDelayMs": 7200000,
          "backoffMultiplier": 2
        }
      }
    }

Impact

Without this, any migration hitting Copilot rate limits will burn through retry budgets on transient errors and likely fail with terminal exhaustion. The workaround is to lower maxParallelAgents to 3-4, but this significantly increases wall-clock time for large codebases.

Related files

  • src/core/agent-launcher.ts — agent invocation and result parsing
  • src/execution/parallel-executor.ts — p-limit concurrency control
  • src/execution/retry.ts — retry logic
  • src/flow/steps/migration.ts — Phase 4 task orchestration
  • src/flow/steps/shared.tsclassifyError(), isTransientModelFailure()

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions