Announcing CIMD support for MCP Client registration
Learn more

Why Long-horizon Agents on Claude Fable 5 Break Your OAuth Tokens

TL;DR

  • Claude Fable 5 is built to run autonomously for days, planning across stages and delegating to sub-agents. That duration guarantees your access tokens expire mid-run instead of merely risking it.
  • The naive build (store an access token, call the API) survives a 20-minute demo because the token never expires inside the demo window. Hand the same agent a multi-day task and it fails.
  • Sub-agent delegation turns a rare refresh race into a routine one: many workers hold the same user credential and hit the same provider at once, and for providers that rotate refresh tokens, one refresh invalidates the token the others are still using.
  • When a token expires mid-run the automation often does not crash. API calls fail silently, the run keeps going, and nobody notices until the output is wrong, because on an unattended multi-day run there is no human watching.
  • Scalekit connectors manage the full token lifecycle (proactive refresh, provider-specific rotation, scoped per-user tokens, and an audit trail) so a days-long agent keeps working and the agent code never holds a raw token.

Every OAuth integration for an agent carries a two-token reality:

Token
Lifetime
Behavior on expiry
Access token
Short, commonly 5 to 60 minutes
Returns a 401; must be refreshed
Refresh token
Longer-lived
Used to mint a new access token without new user consent

A 20-minute demo never crosses the access token expiry boundary, so the "store the token and call the API" model looks correct. It is not correct. It is untested.

Anthropic built Fable 5 specifically to cross that boundary. Its own product materials describe a model that, run inside a harness like Claude Code or Claude Managed Agents, can work for days at a time: planning across stages, delegating to sub-agents, and checking its own work. The canonical examples are large codebase migrations and multi-day autonomous sessions. In early testing, Stripe reported pointing Fable 5 at a 50-million-line Ruby codebase and running the migration in a day.

A run measured in hours or days passes through the access-token expiry boundary many times. Expiry stops being an edge case and becomes the normal operating condition.

Fable's Sub-agent Delegation Turns a Rare Race Into a Routine One

The single-token expiry problem is old and, on its own, tractable. What is new with Fable 5 is concurrency at the credential layer.

Fable 5's headline pattern is fan-out: an orchestrator decomposes a goal and delegates scoped workstreams to sub-agents that run in parallel. Community reports describe sessions spinning up hundreds, and in some cases up to roughly a thousand, parallel sub-agents for codebase-scale work.

Now consider one user's Slack or Google credential under that pattern:

  1. Several sub-agents hold the same access token.
  2. Several of them cross the expiry boundary inside the same short window.
  3. Each independently gets a 401.
  4. Each independently tries to refresh.

If your refresh strategy is reactive (wait for the 401, then refresh), those concurrent refreshes race. For providers that rotate refresh tokens, and Slack is the common example, each successful refresh issues a new refresh token and retires the previous one.

The result: the first sub-agent refreshes and rotates the token. The others are still holding the old refresh token, so their refresh attempts fail against a credential the provider has already retired. One worker wins; the rest cascade into hard authentication failures that no retry can fix.

This is the "3am token expiry race condition" Scalekit has written about before, except Fable 5's architecture triggers it by design rather than by coincidence. The more the model parallelizes, the more reliably it reproduces the race.

Provider behavior is not uniform

This is what makes reactive handling so fragile across a real connector set:

  • Slack: issues rotating refresh tokens; each use retires the prior one.
  • Google: standard one-hour access-token expiry.
  • GitHub PATs: may never expire unless explicitly configured, which lulls you into assuming the pattern generalizes.

A refresh strategy that works for GitHub in the demo silently fails for Slack in production.

Silent Failure Is the Real Cost

When a token expires mid-run, the honest expectation is a crash. The reality is worse: API requests begin failing, often without surfacing as a hard error the agent treats as fatal, and the run continues.

  • On a short supervised task, a human notices the tool stopped responding.
  • On a multi-day unattended run, which is the exact mode Fable 5 was designed for, there is no human in the loop. The agent keeps planning against results it never actually wrote.

The failure then surfaces days later as "why is the output wrong" rather than "auth expired at hour four."

Fable 5 makes this both more likely and more dangerous. More likely, because longer runs cross more expiry boundaries. More dangerous, because the model is explicitly built to operate with minimal oversight, so the watching human is gone by design.

Two More Failure Modes Duration Widens

  • Revocation is not expiry. An access token expiring is routine and recoverable by refresh. A refresh token being revoked (the user withdrew consent, rotated their password, or was offboarded) is not recoverable by any automated retry. It requires explicit re-authorization through the consent flow.

A 20-minute task almost never overlaps a revocation event. A three-day task has a meaningfully larger window in which someone leaves or access is pulled. Your agent has to distinguish "refresh and continue" from "stop and request re-consent," without a human present.

  • Token custody is the second. A long, multi-stage, multi-sub-agent run creates many opportunities for a raw credential to end up somewhere it should never be: the model's context window, a sub-agent handoff payload, a log line, persisted working state.

The rule is simple: the credential should never touch the model. The agent calls a tool, receives a result, and never sees a token. Long-horizon fan-out makes that discipline harder to maintain by hand precisely when it matters most. Keeping credentials in a dedicated token vault is what separates production architectures from fragile prototypes.

The Fix: Manage the Lifecycle Outside the Agent

None of this argues against long-horizon agents. It argues that the token lifecycle has to live in infrastructure, not in the agent code.

The production pattern has five parts:

  1. Refresh proactively, not reactively. Use the expires_in value to refresh ahead of expiry instead of waiting for a 401. This removes the expiry boundary from the agent's hot path.
  2. Serialize refreshes with distributed locking. When many sub-agents share one credential, a lock ensures exactly one refresh happens and the rest wait for the new token. This is what prevents the rotation race.
  3. Keep credentials in a vault, never in the agent. Tokens are stored encrypted, fetched on demand, and never enter the model's context or persisted state. This is a core principle of secure token management for AI agents.
  4. Issue scoped, per-user tokens. The agent acts as the specific user under least privilege, so a multi-day run reads only what that user can read and writes only what they can write. Delegation is captured for audit (the act.sub claim identifies the user the agent acted for).
  5. Handle revocation explicitly. Distinguish an expired access token, which refreshes silently, from a revoked grant, which surfaces as a re-consent request rather than an endless retry.

What this looks like in code

With Scalekit, the agent expresses none of that lifecycle logic. You resolve a connected account for the user, discover the tools available under their grant with list_scoped_tools, and call a tool with execute_tool, passing a connection_name and the user's identifier.

from scalekit import ScalekitClient scalekit = ScalekitClient( env_url=SCALEKIT_ENV_URL, client_id=SCALEKIT_CLIENT_ID, client_secret=SCALEKIT_CLIENT_SECRET, ) # Each sub-agent operates as a specific user, not a shared service account. # The identifier selects whose scoped credential is used. def run_subagent_tool_call(user_identifier: str, tool_name: str, tool_input: dict): # No token handling here. Scalekit resolves, refreshes, and rotates # the credential behind execute_tool, even across a multi-day run. return scalekit.actions.execute_tool( tool_name=tool_name, connection_name="jira", identifier=user_identifier, tool_input=tool_input, ) # Fan out across sub-agents. Fifty concurrent workers can share one user's # credential without racing the refresh endpoint, because the refresh is # serialized in the vault, not in this loop. for action_item in extracted_action_items: run_subagent_tool_call( user_identifier=current_user_id, tool_name="jira_issue_create", tool_input={ "project": action_item["project"], "summary": action_item["summary"], "description": action_item["detail"], }, )

Notice what is absent: no access token, no refresh call, no 401 handler, no per-provider branch for Slack versus Google. The identifier is the only thing that changes whose credential is used, and it is the same line whether the run lasts eighteen minutes or three days.

The Honest Tradeoff

This is buildable in-house, and it is worth saying so plainly. Proactive refresh keyed off expires_in, a distributed lock, a single-retry-then-surface policy, and monitoring on refresh success and failure rates are a known, documented pattern. A capable team can implement it.

The cost is not the first provider. It is the tenth. Each connector brings its own expiry window, rotation behavior, and revocation semantics, and the surface compounds with every tool you add and every sub-agent you run in parallel. You also own the vault, the lock infrastructure, and the audit trail a security review will eventually ask about.

The real build-versus-buy question is not whether you can write proactive refresh once. It is whether you want to maintain provider-specific lifecycle logic, per tenant, at fan-out concurrency, as a permanent part of your codebase. For most teams shipping agents, that is undifferentiated infrastructure.

One More Thing Specific to Fable 5

Fable 5 ships with blocking safety classifiers for dual-use domains such as cybersecurity and biology. When a classifier blocks a request, the Claude API returns a standard HTTP 200 with stop_reason: "refusal" and a stop_details object naming the restriction category, and the recommended pattern is to fall back to another model such as Claude Opus 4.8. Anthropic reports this triggers in fewer than 5% of sessions.

The implication for auth is small but real: when a step routes to a different model mid-run, the credential and tool layer has to survive the switch untouched. If your tokens live in the model's context or are tied to one model's runtime, a mid-run fallback is one more way the run breaks. Keeping the lifecycle in infrastructure makes the model swap a non-event.

FAQs

Why do OAuth access tokens expire in the middle of a long agent run?

Access tokens are deliberately short-lived, commonly 5 to 60 minutes, to limit the damage if one leaks. A Fable 5 run measured in hours or days crosses that boundary repeatedly, so expiry is the normal case, not an edge case. The fix is to refresh proactively using the expires_in value rather than reacting to a 401.

What is the token refresh race condition, and why do sub-agents make it worse?

When several workers share one user's credential and cross the expiry boundary at once, each tries to refresh independently. For providers that rotate refresh tokens on use, the first refresh invalidates the token the others are holding, so their refreshes fail. Fable 5's sub-agent fan-out produces many concurrent workers on one credential, which reproduces the race routinely. A distributed lock that serializes refresh to a single winner resolves it.

Can I just catch the 401 and retry?

Reactive retry is exactly what creates the race at concurrency, and it does nothing for silent failures that never surface as a clean 401. Proactive refresh removes the boundary from the agent's path; a single-retry-then-surface policy handles the residual cases without cascading.

What happens if a user revokes access during a multi-day run?

A revoked refresh token cannot be recovered by automated retry. It requires explicit re-authorization through the consent flow. Your system needs to distinguish an expired access token, which refreshes silently, from a revoked grant, which should surface as a re-consent request. Longer runs widen the window in which revocation and offboarding occur, so this matters more for Fable 5 than for short tasks.

Should the access token ever be in the model's context window?

No. The credential should never reach the model or its sub-agents. The agent calls a tool, receives a result, and never sees a token. Keeping credentials in a vault and fetching them on demand keeps them out of context, logs, and persisted handoff state.

Do I need a platform for this, or can I build it myself?

You can build proactive refresh, distributed locking, and monitoring yourself; it is a documented pattern. The cost is maintaining provider-specific expiry, rotation, and revocation logic across every connector, per tenant, at fan-out concurrency, plus the vault and audit layer. Scalekit provides that lifecycle management behind execute_tool so the agent code holds no tokens and no per-provider refresh logic.

Is this only a Fable 5 problem?

The token lifecycle problem exists for any agent whose run outlasts its access token. Fable 5 makes it unavoidable rather than occasional, because it is explicitly built for multi-day, minimally supervised, sub-agent-heavy runs. The same fix applies to any long-horizon agent regardless of model.

Recommended Reading

Building a long-horizon agent that has to outlive its tokens? Scalekit's connectors handle OAuth, scoped per-user access, and the full token lifecycle so your agent code never touches a credential. Explore the connector catalog at docs.scalekit.com/agentkit/connectors, or talk to an engineer about your setup.

No items found.
Agent Auth Quickstart
On this page
Share this article
Agent Auth Quickstart

Acquire enterprise customers with zero upfront cost

Every feature unlocked. No hidden fees.
Start Free
$0
/ month
1 million Monthly Active Users
100 Monthly Active Organizations
1 SSO connection
1 SCIM connection
10K Connected Accounts
Unlimited Dev & Prod environments