Skip to content

Compaction (/compact) fails with 'No API key found' when model uses codex/claude-cli OAuth runtime #95693

Description

@YUI-TIEN

Summary

/compact (manual and budget-triggered auto-compaction) fails with No API key found for provider "openai" / No API key found for provider "anthropic" when the agent's model is configured to use the codex (or presumably claude-cli) harness runtime via OAuth-style synthetic auth, even though normal (non-compaction) agent turns on the same model/session succeed without any auth error.

Environment

  • OpenClaw version: 2026.6.8 (844f405)
  • OS: macOS 26.5 (arm64)
  • Model config: agents.defaults.models["openai/gpt-5.5"].agentRuntime.id = "codex"
  • Auth: codex provider uses auth_mode: "chatgpt" (ChatGPT/Codex OAuth) stored in the per-agent codex-home/auth.json, not auth-profiles.json. openclaw models status shows Runtime auth: openai via codex uses openai effective=synthetic:codex-app-server | status=usable during normal operation.
  • No anthropic:default / openai:default API-key profile is configured; fallback model anthropic/claude-sonnet-4-6 also has no auth profile (separate known issue, not the focus here, but it does mean both primary and fallback fail the same way during compaction).

Steps to reproduce

  1. Configure an agent's primary model with agentRuntime.id: "codex" and authenticate purely via Codex/ChatGPT OAuth (no openai:default API-key profile in auth-profiles.json).
  2. Run a long-enough session (via Discord channel binding or openclaw agent) until the session approaches agents.defaults.compaction.reserveTokensFloor, or manually trigger /compact.
  3. Observe the failure.

Actual behavior

⚙️ Compaction failed: All models failed (2): openai/gpt-5.5: No API key found for provider "openai". You are authenticated with OpenAI ChatGPT/Codex OAuth. Use openai/gpt-5.5 with the ChatGPT/Codex OAuth profile, or set OPENAI_API_KEY for direct OpenAI API access. (auth) | anthropic/claude-sonnet-4-6: No API key found for provider "anthropic". ...

Gateway log (compaction-diag) shows both trigger=budget (automatic) and trigger=manual (/compact) attempts failing identically:

[compaction-diag] end ... diagId=cmp-... trigger=budget provider=openai/gpt-5.5 attempt=1 maxAttempts=1 outcome=failed reason=unknown detail=No_API_key_found_for_provider_openai_._You_are_authenticated_with_OpenAI_ChatGPT/Codex_OAuth...

Meanwhile, in the same session, regular (non-compaction) turns against openai/gpt-5.5 succeed normally using the same codex OAuth auth (winnerProvider: openai, result: success, runner: embedded), confirming the codex OAuth credential itself is valid and usable outside of the compaction path.

Expected behavior

Compaction should be able to use the same codex/claude-cli synthetic/OAuth auth path that regular agent turns use, rather than requiring a separate plain API-key profile.

Root cause investigation (source-level, not just log inspection)

Traced through the bundled dist code (compact-j1-PEvXo.js, model-auth-Aq6KQDp8.js, compaction-runtime-context-DTO0IG8s.js, harness-runtimes-BbJCcEVN.js, model-runtime-policy-DmfWB8cz.js):

  1. compact-j1-PEvXo.js calls getApiKeyForModel(...)resolveApiKeyForProvider(...). When called with a profileId, this function does correctly check shouldDeferSyntheticProfileAuth(...), which is designed to defer to synthetic/OAuth auth (e.g. codex-app-server) instead of failing immediately.
  2. The profileId passed in comes from resolvedCompactionTarget.authProfileId, computed by resolveEmbeddedCompactionTarget(...) in compaction-runtime-context-DTO0IG8s.js.
  3. That function only engages codex-runtime-aware resolution (shouldUseCodexRuntimeProviderForCompaction) when params.harnessRuntime === "codex".
  4. harnessRuntime is computed in compact-j1-PEvXo.js as selectedHarnessRuntime = params.agentHarnessId ?? configuredHarnessRuntime, where configuredHarnessRuntime comes from resolveAgentHarnessPolicy(...) in harness-runtimes-BbJCcEVN.js, gated by:
    const configuredHarnessRuntime = configuredHarnessPolicy.runtimeSource
      && configuredHarnessPolicy.runtimeSource !== "implicit"
      && !isDefaultAgentRuntimeId(configuredHarnessPolicy.runtime)
      ? configuredHarnessPolicy.runtime
      : void 0;
  5. resolveAgentHarnessPolicy ultimately calls resolveModelRuntimePolicy(...) in model-runtime-policy-DmfWB8cz.js, which should match the configured agents.defaults.models["openai/gpt-5.5"].agentRuntime = {id: "codex"} entry and return source: "model" (i.e. not "implicit").

Statically, this chain looks like it should resolve harnessRuntime: "codex" correctly from the documented config shape above, which would in turn let shouldDeferSyntheticProfileAuth kick in and avoid the hard failure. But in practice the compaction call still fails as if harnessRuntime were never "codex" (or as if the codex synthetic-auth deferral never returns a usable key). I was not able to pin down the exact point of divergence between the static code path and actual runtime behavior without adding instrumentation/debug logging inside the installed package, which I avoided doing on a teammate's production install.

Suggested next steps for maintainers

  • Add a debug/trace log line in resolveEmbeddedCompactionTarget and shouldDeferSyntheticProfileAuth printing the resolved harnessRuntime, authProfileId, and the outcome of the synthetic-auth deferral, to make this divergence directly observable.
  • Consider whether resolveModelRuntimePolicy's sessionAgentId resolution (via resolveSessionAgentIds) might resolve to an agent id that doesn't match the one holding the agentRuntime override in this scenario (e.g. when invoked from the compaction path vs. a normal turn), which would silently fall through to source: "implicit".

Workaround in use

Adding a plain API-key auth profile (openclaw models auth paste-api-key --provider openai or --provider anthropic) alongside the OAuth profile avoids the failure, since getApiKeyForModel succeeds via the plain API key path before ever needing the synthetic-auth deferral. This is a viable workaround but defeats the purpose of using Codex/ChatGPT OAuth exclusively (extra cost, separate billing).

Metadata

Metadata

Assignees

Labels

P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:needs-live-reproClawSweeper needs live local, crabbox, or manual validation to confirm this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.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: 🐚 platinum hermitGood issue quality with a plausible reproduction path needing some confirmation.

Type

No type

Fields

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions