Skip to content

429 in streamable-http-transport.ts doesn't inspect Retry-After — incorrect retry behavior #96

@SirBrenton

Description

@SirBrenton

Summary

Finding

Ran pitstop-check against the MCP transport layer:

src/transports/streamable-http-transport.ts:153 — 429 handled without Retry-After

Why it matters

In MCP transport, a 429 can mean different things:

  • Transient pressure → wait and retry
  • Quota exhaustion → do not retry

When Retry-After is ignored, the transport retries quota exhaustion cases that can't recover until the window resets.

Example

curl -s -X POST https://web-production-273d3.up.railway.app/classify \
  -H "Content-Type: application/json" \
  -d '{"status":429,"headers":{"retry-after":"600"}}'

Returns STOP — do not retry.

Static check: github.com/SirBrenton/pitstop-check

Reproduction steps

  1. Clone the repo and run pitstop-check against the transport layer:
npx pitstop-check src/transports/streamable-http-transport.ts
  1. Observe warning at line 153:
    429 handled without Retry-After — likely incorrect retry behavior

Expected behavior:
429 responses with a Retry-After header should use that value to determine wait time before retry. Quota exhaustion cases (long Retry-After) should not retry at all.

Actual behavior:
All 429s handled uniformly without inspecting Retry-After.

No distinction between transient pressure and quota exhaustion.

Environment

Static analysis only — no runtime reproduction required.

  • OS: macOS
  • Node: current LTS
  • Transport: streamable-http-transport.ts
  • Finding confirmed in current main branch (cloned 2026-04-02)
  • Client: n/a (static code analysis)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions