Skip to content

CLI auth flow silently swallows REST quota / rate-limit errors during token validation, surfaces them as 'Session was not created with authentication info or custom provider' #3311

@loganrosen

Description

@loganrosen

Describe the bug

When the configured SDK auth token (--auth-token-env / COPILOT_SDK_AUTH_TOKEN) hits any non-200 response at the validation step (GET /copilot_internal/user), the auth bootstrap silently fails. With --no-auto-login set (typical for headless SDK use), no other auth method runs, the Session is created without authentication, and the first model-touching operation surfaces as the misleading:

Execution failed: Error: Session was not created with authentication info or custom provider

The most common trigger in real workloads is GitHub's standard 5000 req/hr REST quota being exhausted on a service-account PAT shared across multiple consumers. When that bucket is empty, /copilot_internal/user returns 403 "API rate limit exceeded for user ID …". There is no retry on 403/429 at the auth-validation step, no surfacing of the underlying reason to the SDK caller, and no Retry-After honoring.

Confirmed reproducible against @github/copilot-sdk 0.3.0 with bundled CLI 1.0.36-0, and behavior is unchanged through CLI 1.0.47.

Reproduction

  1. Take any GitHub user PAT with Copilot access.
  2. Burn through its 5000 req/hr REST quota (easy via parallel curls or by running enough SDK sessions back-to-back).
  3. With quota exhausted, spawn a Copilot SDK session in headless mode using that PAT via --auth-token-env/COPILOT_SDK_AUTH_TOKEN and --no-auto-login:
    from copilot.client import CopilotClient, SubprocessConfig
    from copilot.session import PermissionHandler
    client = CopilotClient(SubprocessConfig(github_token=PAT, log_level="info"))
    await client.start()
    session = await client.create_session(
        on_permission_request=PermissionHandler.approve_all,
        model="claude-opus-4.6",
        system_message={"mode": "replace", "content": "hi"},
    )
    await session.send("ping")  # raises with the misleading "Session was not created…" error
  4. Direct probe with the same token to confirm the underlying cause:
    GET https://api.github.com/rate_limit
    Authorization: Bearer <PAT>
    → 200, {rate: {limit: 5000, used: 7912, remaining: 0, reset: ...}}
    

Evidence

~/.copilot/logs/process-*.log shows the swallowed error:

[ERROR] Failed to validate SDK token (403): API rate limit exceeded for user ID <REDACTED>. ...

But the only thing surfaced to the SDK caller is the eventual "Session was not created with authentication info or custom provider", which is not actionable — the user has no way from the SDK-side error to know it was actually a transient REST quota hit.

Three things compound to make this confusing

  1. Auth validation only retries HTTP/2 transport errors. A 403 / 429 / 5xx throws immediately on the first attempt, no backoff, no Retry-After parsing.
  2. The validation failure is logged to the log file but not propagated through the SDK boundary. Callers using the Python SDK (and presumably others) get no signal at client.start() / create_session() that auth bootstrap failed.
  3. The eventual "Session was not created…" error is generic. It surfaces later when the session tries to do its first model-touching operation, with no hint of the real upstream reason.

Suggested fixes

  1. Differentiate 429 / 403-with-x-ratelimit-remaining: 0 from real auth failures at the validation step. Either retry with respect to x-ratelimit-reset, or surface a typed RateLimitedError through the SDK boundary.
  2. Surface auth-bootstrap failures through the SDK API. Today they only land in the log file; the caller has no way to know.
  3. Improve the user-facing error message when the session ends up unauthenticated due to a known prior failure: include the upstream status / reason instead of the generic "Session was not created with authentication info or custom provider".

Affected version

GitHub Copilot CLI 1.0.36-0 (bundled with @github/copilot-sdk 0.3.0) through 1.0.47. Behavior reproduces identically across this range.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    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