Skip to content

Session resume blocks on MCP OAuth tokens that cannot be refreshed, timing out after 60s instead of failing fast #3640

Description

@nadineshmo

App version

1.1.15 (Windows)

OS

Windows 11 Enterprise 10.0.26100 (build 26100), AMD64

Summary

When a cached MCP OAuth token has expired and has no refresh token, it
cannot be renewed without interactive sign-in. On session resume the app still
attempts a cached-token reconnect for each such server and waits, rather than
recognising the token as unrenewable and failing fast. With enough of these,
resume exhausts its 60s ceiling and fails.

Steps to reproduce

  1. Configure one or more MCP servers that authenticate via OAuth.
  2. Let their cached tokens expire without completing a re-auth prompt. Tokens
    accumulate in ~/.copilot/mcp-oauth-config/*.tokens.json; the ones that
    matter have a past expiresAt and no refreshToken field.
  3. Restart the app and resume an existing session.

Count the unrenewable tokens:

$now = Get-Date
Get-ChildItem "$env:USERPROFILE\.copilot\mcp-oauth-config" -Filter *.tokens.json |
  ForEach-Object {
      $j = Get-Content $_.FullName -Raw | ConvertFrom-Json
      if (-not $j.expiresAt) { return }
      # expiresAt is Unix seconds on Windows 1.1.15; handle ISO strings too.
      $exp = if ($j.expiresAt -is [string]) { [datetime]::Parse($j.expiresAt) }
             else { [DateTimeOffset]::FromUnixTimeSeconds([int64]$j.expiresAt).LocalDateTime }
      if ($exp -lt $now -and -not $j.refreshToken) { $_.Name }
  } | Measure-Object | Select-Object -ExpandProperty Count

Expected behavior

A token that is expired and has no refresh token is known-unrenewable before
any network call. Resume should skip it immediately (surfacing "needs sign-in"
for that server) and continue. One unreachable MCP server should not be able to
delay or fail session resume.

Actual behavior

The app attempts a cached-token reconnect per server and waits. Once enough of
them cannot answer, resume hits its ceiling and the session fails to open:

WARN  session::manager::lifecycle: session resume timed out; releasing CLI and surfacing error
WARN  session::manager::cli_pool:  force-retiring pooled CLI process
ERROR handlers::session:            extensibility RPC failed method=session.mcp.list error=request cancelled
ERROR handlers::session:            failed to resume session error=operation timed out:
                                    session resume timed out after 60s

I had 51 of 53 cached tokens expired, 45 of them with no refresh token.
Every resume of a previously working session failed at exactly 60s.

Impact — it cascades

The failure is not contained to one resume:

  1. Resume times out at 60s and the pooled CLI process is force-retired.
  2. The UI appears to hang. Windows logged AppHangB1 for github.exe 1.1.15
    twice within seven minutes, both under the same fault bucket
    (1215446218445078001) — so it is reproducible, not a one-off.
  3. The natural user response is to end the task in Task Manager, which kills
    only the root process and orphans its children (see companion issue about
    there being no way to fully quit the app).
  4. The next launch starts a fresh set of child processes on top of the strays.

After a few cycles I had ~23 app-related processes holding 4.3 GB, with
free RAM down from 12 GB to 7 GB. Every new session I opened was also affected,
so the app looked broadly broken rather than "one MCP server needs sign-in".

Fix that worked

Deleting only the expired token files resolved it completely:

before after
session resume timed out at 60s 6.6s – 8.2s
unreachable MCP server consumed the resume budget fails in < 1s
resume timeouts in log repeated 0

Same MCP servers, same config — only the dead tokens removed. That points at
the retry/wait path rather than the servers themselves.

Suggested fixes

  1. Fast-fail unrenewable tokens. If expiresAt is past and there is no
    refreshToken, skip the reconnect attempt entirely and mark the server as
    needing sign-in. This requires no network call to determine.
  2. Don't let MCP reconnect block resume. Resume the session first and let
    MCP servers attach asynchronously, or give MCP a budget well under the
    overall resume ceiling.
  3. Surface it in the UI. "3 MCP servers need sign-in" is actionable;
    a hang is not. Nothing in the UI indicated auth was the problem — I only
    found it by reading the logs.
  4. Prune or flag stale tokens. Tokens accumulated to 53 files over months
    with no visible signal. Even a warning at a threshold would have prevented
    this.

Workaround

Delete expired token files (valid ones are preserved):

$now = Get-Date
Get-ChildItem "$env:USERPROFILE\.copilot\mcp-oauth-config" -Filter *.tokens.json |
  Where-Object {
      $j = Get-Content $_.FullName -Raw | ConvertFrom-Json
      if (-not $j.expiresAt) { return $false }
      $exp = if ($j.expiresAt -is [string]) { [datetime]::Parse($j.expiresAt) }
             else { [DateTimeOffset]::FromUnixTimeSeconds([int64]$j.expiresAt).LocalDateTime }
      $exp -lt $now
  } | Remove-Item

Servers you actually use will prompt to re-authenticate on next launch.

Logs

Happy to attach /collect-debug-logs output if useful.

Activity

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