Skip to content

Provider-level credential discovery ignores auth.order, OAuth preference, and expiry — silently breaks ChatGPT-mode background paths #110488

Description

@ari-hjunk

Provider-level credential discovery ignores auth.order, OAuth preference, and expiry — silently breaks ChatGPT-mode background paths

  • OpenClaw version: 2026.7.1 (2d2ddc4)
  • Platform: macOS (gateway as LaunchAgent), Node 22.x
  • Component: auth storage / model registry / background (non-interactive) credential resolution

Summary

The automatic/background credential path resolves credentials by provider
name
from a one-credential-per-provider map, built by iterating the
credential store's profiles in raw store order and keeping the first
credential found for each normalized provider. No OAuth-over-api_key
preference, no auth.order, and no expiry check is applied on this path. The
profile-id-aware resolution used by manual RPCs is not consulted.

Call chain (2026.7.1 dist):

  • discoverAuthStorageresolveAgentCredentialMapFromStore
    ("build one credential per normalized provider") —
    agent-model-discovery-Bq74MwWh.js:63-73
  • background consumers: context-engine compaction safeguard →
    resolveModelAuthmodelRegistry.getApiKeyAndHeaders(model)
    authStorage.getApiKey(model.provider)
    attempt.model-diagnostic-events-Dg8sP6iR.js:2790
  • ChatGPT-mode driver JWT-decodes the resolved apiKey to build its
    chatgpt-account-id header — openai-chatgpt-responses-DVC4Bk_A.mjs:1015-1017

Expected behavior

Background/automatic paths resolve credentials with the same preference logic
as interactive/profile resolution: OAuth preferred over api_key, auth.order
respected, expired credentials skipped — or, when the session carries an
authProfileOverride, background paths route through profile-id resolution.

Actual behavior

The first credential in raw store order for the provider wins unconditionally.
If that credential is unusable for the driver the session actually runs on,
every background operation for that provider fails, while the identical manual
operation succeeds.

Reproduction steps

  1. Create two auth profiles for one provider in the credential store, ordered
    so an api_key profile precedes an OAuth profile (raw store order).
  2. Run an agent on a ChatGPT-mode model (codex agent runtime /
    openai-chatgpt-responses driver), which authenticates via the OAuth
    profile.
  3. Let a session grow past the compaction budget threshold so the automatic
    compaction safeguard fires (any other background/non-interactive model call
    for that provider also works as a trigger).
  4. Observe: automatic compaction fails on every attempt with
    Failed to extract accountId from token (the driver JWT-decodes the
    api_key, which is not a JWT), with retryAsync ×3 bursts — while a manual
    sessions.compact on the same session succeeds via profile resolution.

Impact

Observed on a production single-user deployment (2026-07-15/16): ~24
errors/day from failed automatic compactions; sessions could no longer be
compacted automatically at all. Two secondary hazards:

  • The repeated background failures fed auth-profile rotation, which
    auto-pinned the dead profile onto live sessions (self-poisoning; the
    sessions then failed until the pins were cleared manually).
  • An expired OAuth credential also passes conversion on this path and
    would be refresh-attempted from an in-memory copy — a refresh-token-reuse
    hazard for any deployment holding two profiles of one provider.

Suggested fix

Build the discovery map with the same preference logic as profile resolution
(OAuth preferred, auth.order respected, expired credentials skipped), or
route background paths through profile-id resolution when the session carries
an authProfileOverride.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions