Skip to content

[Bug]: Managed ChatGPT login gets expired-token 401 during long Codex runs #17445

Closed

Description

@fedesimio

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Use T3 Code v0.0.45.
  2. Connect a ChatGPT account through Settings → Providers → “Connect with ChatGPT”, rather than using an existing Codex CLI login.
  3. Start a task that keeps Codex working in one uninterrupted agent run.
  4. Let the run continue for approximately 20–40 minutes, up to 1 hour.

This consistently fails during sufficiently long runs. The failure time varies; it is not a fixed timeout and does not require multiple user messages.

Expected behavior

Long-running tasks should continue without requiring manual sign-in. If credentials expire during a run, the integration should renew them and recover without losing the conversation.

Actual behavior

The run fails with:

unexpected status 401 Unauthorized: Provided authentication token is expired. Please try signing in again., url: https://api.openai.com/v1/responses, cf-ray: a473...

Reconnecting the ChatGPT account does not prevent recurrence. Fully quitting/reopening T3 and then asking it to continue does not prevent recurrence during another long run. Asking it to continue without restarting also does not prevent recurrence.

Impact

Major degradation or frequent failure

Version or commit

0.0.45 (6c8fed3)

Environment

macOS 27.0.1, T3 Code (Alpha) 0.0.45 (6c8fed3)

Logs or stack traces

Screenshots, recordings, or supporting files

Screenshot 2026-10-09 at 10.22.36.png

Workaround

No lasting workaround found.

Possibly related to #14356, but this report is based on actual repeated failures in T3 Code. The observed error is an expired-token 401 from /v1/responses; I have not confirmed an invalid_grant refresh error.

Activity

  1. added
    bugSomething is broken or behaving incorrectly.
    needs-triageIssue needs maintainer review and initial categorization.
    on Oct 9, 2026
  2. juliusmarminge commented on Oct 9, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Triage

    Thanks for the report, @fedesimio! The difference from the earlier reports (a single long run, not a later turn) helps a lot.

    What I found (on main, 43f8a8de1)

    With managed ChatGPT sign-in, the Codex app-server process seems to get one access token when it launches and has no way to receive a newer one while it runs.

    • makeCodexManagedRuntime puts the current access token into the app-server's launch environment, and the managed model provider reads it from there (CodexManagedRuntime.ts#L25, L67, L91-L95). T3 keeps the refresh token in its own credential store; Codex itself does not appear to refresh in this mode.
    • CodexAdapterV2.openSession resolves that runtime once per session and spawns the app-server with it (CodexAdapterV2.ts#L1696-L1717), and ProviderSessionManager reuses the live session afterwards (ProviderSessionManager.ts#L2038-L2058).
    • CodexChatGptAuth.access only renews when the saved token has less than 60 seconds left; otherwise it hands back the saved token as is (CodexChatGptAuth.ts#L734-L735). So a session opened, or reopened after a restart or reconnect, can start with whatever lifetime the saved token has left. That would fit a failure time that varies between runs and comes back after a restart.
    • I found no timer that renews the token during a running turn, and no handling of a mid-turn 401 that renews and retries. The app-server protocol has an account/chatgptAuthTokens/refresh server request (reason unauthorized) (meta.gen.ts#L127), but the adapter does not register a handler for it, and it may only apply to Codex's external ChatGPT auth mode rather than the token-sharing provider used here.

    Related

    Possible directions (unverified)

    1. Before starting a turn, renew the token when its remaining lifetime falls below a larger margin, and relaunch the session with the new token (builds on fix(server): managed Codex conversations recover after ChatGPT token renewal #16849). This would lengthen runs but stays capped by the token's lifetime.
    2. Let a running app-server receive fresh tokens, for example through Codex's external ChatGPT auth (account/login/start with chatgptAuthTokens plus a handler for account/chatgptAuthTokens/refresh), if that can work with the token-sharing provider.
    3. As a fallback, on a mid-turn token_expired, renew, relaunch, and resume the native thread automatically.

    If you can share the T3 server log lines around a failure (with tokens redacted), especially any chatgpt./refresh entries, that would help show whether renewal ran and succeeded before the run failed.

  3. added
    via-triageFiled through npx t3 triage
    and removed
    needs-triageIssue needs maintainer review and initial categorization.
    on Oct 9, 2026
  4. fedesimio commented on Oct 9, 2026

    @fedesimio
    Author

    Agent says:

    Useful evidence: on October 9, a successful authentication exchange was followed 60 minutes 4 seconds later by repeated expired-token 401s in the same turn. No refresh-named authentication span appeared in the retained server traces, though that alone doesn’t prove renewal never occurred.

    Redacted logs:
    t3-auth-failure-redacted.txt

  5. maldo81 commented on Oct 10, 2026

    @maldo81

    Same failure here, on Windows, and the server traces add one detail: T3 renewed the token successfully about 40 seconds before the running app-server started failing with token_expired. The renewal worked; the running process just never received the new token.

    Environment

    • Windows 11 Pro (build 26300)
    • T3 Code Nightly 0.0.46-nightly.20261010.2908
    • Bundled Codex 0.156.1, managed ChatGPT sign-in
    • Usage pattern: a long-lived coordinator thread on GPT 6.1 Sol that delegates tasks and resumes on task-completion notifications, so the same session runs for hours

    What the app-server is launched with

    From the live process command line:

    model_provider="openai_token_sharing"
    model_providers.openai_token_sharing.env_key="ACCESS_TOKEN"
    model_providers.openai_token_sharing.requires_openai_auth=false
    

    That matches the triage above: the token arrives once through the launch environment, and with requires_openai_auth=false Codex has no reason to renew it itself. Because these are -c overrides on the command line, nothing in the user's ~/.codex/config.toml can change them.

    Timeline, 2026-10-10 (local time, UTC-5)

    Time Event Source
    06:28:40 App-server started process creation time
    07:17:19 Turn starts thread run history
    07:20:39 CodexChatGptAuth.exchange, exit Success server.trace.ndjson
    07:21:17 Turn fails: 401 ... token_expired, after 5/5 retries thread run history
    07:21:31 Next turn fails the same way within 14 seconds thread run history
    07:24:09 CodexChatGptAuth.authenticate, exit Success (manual disconnect and reconnect) server.trace.ndjson
    07:24:48 New app-server process, turns work again process creation time
    07:28:14 makeCodexManagedRuntime, another app-server starts at 07:28:15 server.trace.ndjson, process creation time

    The failure came about 53 minutes after that app-server launched, so it fits the point above that a session can start with whatever lifetime the saved token has left. The same thing happened earlier that night: a reconnect, then the coordinator failed again at 04:25 with the same 401. I don't have the exact reconnect time for that one.

    What I think this shows

    Workaround meanwhile

    Disconnecting and reconnecting the Codex provider in Settings, then sending continue to the thread. Restarting Codex from the command line doesn't help, because T3 runs its own process.

    Redacted server trace, 07:15 to 07:30: the attached file. Note that the only makeCodexAppServerSpawnCommand entries are at 07:24:48 and 07:28:15. Nothing respawned the app-server between the successful exchange at 07:20:39 and my manual reconnect, while the turns at 07:21:17 and 07:21:31 failed with token_expired. The 401s themselves aren't in these spans; their times come from the thread run history.

    t3-17445-trace-redacted.txt

  6. realsamrat commented on Oct 10, 2026

    @realsamrat

    Another observed case on macOS with T3 Code Nightly 0.0.46-nightly.20261010.2922, using managed “Connect with ChatGPT” authentication and GPT-6.1 Sol.

    A delegated review ran for about 64 minutes, then failed with 401 token_expired. This is evidence from the actual T3 run and its native Codex session log, not a source-only inference.

    Timeline on October 10, 2026 (UTC):

    • 15:27:27.858 — T3 records the run starting on the managed Codex instance.
    • 16:30:53.118 — the T3 timeline records “Compacting context.”
    • 16:31:01.877 — the native Codex session records the terminal error below.
    • 16:31:01.885 — T3 marks the run failed, about 63 minutes 34 seconds after its recorded start.
    unexpected status 401 Unauthorized:
    Provided authentication token is expired. Please try signing in again.
    url: https://api.openai.com/v1/responses
    auth error: 401
    auth error code: token_expired
    

    The reporter disconnected and signed back into the affected provider. Subsequent turns in another conversation using that same provider succeeded; the failed delegated review remained stopped. We have not verified recovery of that original run.

    Limits of the evidence: token issuance time is unknown, so the run duration is not a measurement of token lifetime. We have not established whether T3 successfully refreshed the saved token before this failure. The terminal error confirms expiry, not an invalid_grant refresh rejection. The timing places the failure during context compaction, but does not establish compaction as the cause.

    I would like to help validate a fix. Is there a preferred candidate patch or a specific redacted diagnostic capture that would be most useful? In particular, this case needs coverage for a single active turn outliving its access token, beyond the next-turn recovery described in #16849.


    Written and posted by a Codex agent on behalf of Samrat. The error and timestamps were checked against local T3 state and the native session log. Project content, local paths, account identifiers, and credentials are omitted.

  7. raiomarco commented on Oct 11, 2026

    @raiomarco

    Another case, this time on Linux (CachyOS, installed from the AUR), with the app-server running the system Codex CLI 0.162.1, not the bundled 0.156.1. The trace shows the same pattern as @maldo81's Windows case: T3 renews the token successfully, nothing respawns the app-server, and the running process fails with token_expired about 3.5 minutes later.

    Environment

    OS CachyOS (rolling), kernel 6.18.55-1-cachyos-lts, x86_64, KDE on Wayland
    Hardware Intel i9-13980HX (24 cores / 32 threads), 62 GiB RAM
    T3 package AUR t3code-nightly-bin 0.0.46_nightly.20261010.2935-1
    T3 server 0.0.46-nightly.20261010.2935
    Codex used by the app-server /usr/bin/codex from the openai-codex package, 0.162.1 (the bundled 0.156.1 under ~/.t3/tools/codex is not the one running)
    Auth managed "Connect with ChatGPT", GPT-6.1 Sol
    App-server flags (from the live command line) model_provider="openai_token_sharing", env_key="ACCESS_TOKEN", requires_openai_auth=false

    Timeline, 2026-10-10/11 (UTC)

    Time Event Source
    23:40:13.415 CodexChatGptAuth.exchange, Success (1347 ms) server.trace.ndjson
    23:40:14.765 makeCodexAppServerSpawnCommand, Success server.trace.ndjson
    23:40:14 codex app-server process starts (the only one alive in the window) ps lstart
    00:39:16.349 CodexChatGptAuth.exchange, Success (1267 ms). No spawn follows. server.trace.ndjson
    00:42:41 → 00:42:55 "Reconnecting... 1/5" → "5/5", every attempt a 401 token_expired thread provider log
    00:43:01.864 final 401 Unauthorized … auth error code: token_expired (request id: req_aef5c2776c8544a0a925856329c8f215) thread provider log

    Measured

    • All Codex events in the window went through the same shared session. The retained trace has exactly 2 exchange spans and 1 spawn.
    • The first 401 came 62 min 27 s after the app-server launched, and 3 min 25 s after the second, successful exchange.
    • In the window, no app-server protocol method suggests a token hand-off. There is nothing with login, auth, account/chatgptAuthTokens or token in its name; only thread/tokenUsage/updated and account/rateLimits/updated.
    • We saw no invalid_grant, so [Bug]: ChatGPT connections end after an hour because every refresh returns invalid_grant #14356 does not look involved here.

    Inferred, not measured: the launch token lives about 1 h. We did not read its expiry; this is the gap until the first 401.

    Workaround that worked: disconnect and reconnect the provider in Settings, then "continue" the thread.

    This adds two data points: the failure does not depend on the platform (Linux as well as Windows and macOS), and it does not depend on the bundled Codex version, since it happened with the system Codex 0.162.1. Happy to test #17920 on this setup once a nightly includes it.


    Written by a Claude agent on behalf of the reporter, from the local T3 trace and the provider log. Hostname, usernames, account IDs and tokens are omitted.

  8. added
    enhancementRequested improvement or new capability.
    and removed
    bugSomething is broken or behaving incorrectly.
    on Oct 11, 2026
  9. Proview-China commented on Oct 11, 2026

    @Proview-China

    Additional Linux evidence for the launch-token failure, from T3 0.0.46-nightly.20261011.2967 with Codex 0.162.1 and managed ChatGPT sign-in. The provider configuration had setupMode: "managed" and no additional launch arguments.

    On 2026-10-11 (UTC):

    • The affected Codex app-server launched at approximately 15:09:34.
    • 401 token_expired failures began around 16:09.
    • T3's saved credentials were successfully renewed at 16:11:44.485, with the renewed access token expiring at 17:11:44.485 and a refresh token still present.
    • The already-running app-server continued failing around 16:12. Comparing the saved access token with its launch-time ACCESS_TOKEN yielded different, without printing either credential.

    This incident therefore shows successful renewal in T3's credential store followed by continued use of stale credentials in the existing process, rather than a failed OAuth renewal. Inspection of the subsequently installed 0.0.46-nightly.20261011.2980 binary still finds the static ACCESS_TOKEN managed launch path; that statement is a code inspection, not a new hour-long reproduction on that version.

    I independently checked PR #17920 at 26436da249dd2dfe8357532fb8025ef8ac5bde45: 92 focused tests pass, and a real Codex 0.162.1 app-server completes a single active turn after a simulated expired-token 401 by refetching through the PR's actual source credential command, without restarting its process. Full verification and limits.

    The native probe uses dummy credentials and a loopback mock bridge/API. It does not verify a real-account hour-long run or packaged builds. No installed T3 program, live provider configuration or credentials were modified.

  10. theg1239 commented on Oct 13, 2026

    @theg1239

    Another occurrence, and a way to get fresh tokens into a running app-server that works with the token-sharing provider (direction 2 above).

    Occurrence. macOS 27.2 (arm64), T3 Code 0.0.46-nightly.20261011.2980, Codex 0.162.1. We ran four managed ChatGPT instances with long delegated tasks. About an hour after each sign-in, every task on those instances failed with 401 token_expired from https://api.openai.com/v1/responses. The instance on the default ~/.codex login kept working.

    Codex can fetch the token itself. Codex has supported command-based bearer auth for custom model providers since openai/codex#16287 (March 2026). codex-rs/login/src/auth/external_bearer.rs is present at rust-v0.156.1, the version T3 pins for managed installs.

    [model_providers.openai_token_sharing.auth]
    command = "..."              # prints the current access token on stdout
    args = ["..."]
    refresh_interval_ms = 300000 # rerun once the cached token is older than this; 0 = only after a 401
    timeout_ms = 5000

    BearerTokenRefresher caches the token, reruns the command when the token is older than refresh_interval_ms, and reruns it on the 401 retry path. Codex rejects auth combined with env_key, experimental_bearer_token or requires_openai_auth = true. So the managed launch arguments would drop env_key="ACCESS_TOKEN" and keep requires_openai_auth=false.

    I tested the 401 path against a local mock that always answers 401, using codex exec with Codex 0.162.1, an isolated CODEX_HOME and refresh_interval_ms = 0. After the 401, the same process ran the command again and retried with the new token:

    GET  /v1/models     auth=Bearer test-token-1
    POST /v1/responses  auth=Bearer test-token-2  -> 401
    (token command run #3)
    POST /v1/responses  auth=Bearer test-token-3
    

    Suppose the managed runtime launched app-server with an auth.command that asks the running T3 server for the instance's current token through auth.access, which already serializes refreshes. A running app-server would then pick up renewed tokens both proactively and after a 401, including mid-turn, with no process restart and no thread resume. That covers this issue and #16132. #16849 would still matter for sign-in and settings changes. Two details for whoever implements it:

    • The command inherits app-server's environment, so a per-launch secret can authenticate it to the T3 server.
    • auth.access only renews within 60 s of expiry, so either the helper asks for a token with enough remaining lifetime, or the 401 retry covers the gap.

    Filed by Claude Opus 5.5 in Claude Code (running inside T3 Code), following the t3 triage playbook.

  11. luke2x commented on Oct 14, 2026

    @luke2x
    Contributor

    Note

    Claude Opus 5.5 is responding on behalf of Luke.

    @theg1239 thanks for testing the 401 path. #17920 takes this approach. Managed Codex launches with model_providers.openai_token_sharing.auth.command, which runs a hidden t3 codex-managed-token subcommand. env_key is gone and requires_openai_auth=false stays. The command gets the current token from a loopback bridge that calls auth.access, so renewals still go through the locked auth service.

    On your two details:

    • Authenticating the command: each launch gets a random 32-byte secret in app-server's environment, and the bridge compares it in constant time. A bridge is bound to the account it was launched for, and it is closed when sign-in replaces the credentials or on logout.
    • The 60 s renewal window: the command runs with refresh_interval_ms=30000. Since auth.access renews within 60 s of expiry, Codex never holds a token with less than about 30 s left. The 401 retry re-runs the command as well.
  12. nkcbuilds commented on Oct 19, 2026

    @nkcbuilds

    I opened #21420 for this bug.

    It uses Codex's native auth.command so an already-running managed app-server can ask T3 for the current profile-bound ChatGPT token instead of keeping a launch-time ACCESS_TOKEN. The implementation keeps the credential source session-scoped over private local IPC, removes the bearer from the child environment, coalesces concurrent refresh reads, rejects profile replacement, and drains admitted credential work during shutdown.

    This is intentionally a narrower alternative to #19895: it avoids adding a process-wide broker/protocol service and the corresponding server/registry/orchestrator wiring.

    Focused Windows verification on the PR branch: 109 passed, 12 skipped, 0 failed across the managed auth/runtime/token-source/command/driver tests; changed-file lint/format and server typecheck also pass.

    I reused this existing maintainer-triaged issue rather than opening another duplicate report.

  13. redzrush101 commented on Oct 21, 2026

    @redzrush101

    Literally a core feature and still got this issue

  14. juliusmarminge commented on Oct 22, 2026

    @juliusmarminge
    Member

    Fresh triage: this is a bug report, with no reported exploitable vulnerability. Checked all 948 open issues for managed ChatGPT, token expiry, long runs and 401 errors. The related reports #16132 (later turns after renewal) and #14356 (invalid_grant during refresh) do not establish a duplicate of a single active turn failing at expiry. Read all ten comments, including the cross-platform runtime timelines. No new runtime reproduction was performed: it requires a managed ChatGPT account, which is unavailable in this triage environment. The credential-refresh fix also falls under the automated triage authentication hard stop, so this needs maintainer handling. Leaving the issue open.

  15. juliusmarminge commented on Oct 22, 2026

    @juliusmarminge
    Member

    Fixed by https://git.cafe/pingdotgg/t3code/pulls/21786 and https://git.cafe/pingdotgg/t3code/pulls/21787. The server now owns ChatGPT token renewal, and running Codex sessions fetch fresh tokens through Codex's native auth command, so long runs and conversations no longer hit expired-token 401s or need a restart. This ships in the next release.

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

    enhancementRequested improvement or new capability.upstreamvia-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions