openclaw - 💡(How to fix) Fix Fix OpenAI Codex OAuth routing for embedded compaction fallback [3 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Embedded Pi runs already normalize provider=openai plus an openai-codex:* auth profile to the openai-codex runtime provider. Embedded compaction fallback used a separate target resolver and did not apply the same routing rule.

That can produce an inconsistent compaction runtime context:

  • provider=openai
  • authProfileId=openai-codex:...
  • model=gpt-*

The main run path works, but the compaction/context-engine fallback can enter the plain OpenAI API-key path and fail with a missing OpenAI API key, even though the active session is using Codex OAuth.

Observed failure shape:

codex app-server compaction could not use thread binding
...
No_API_key_found_for_provider_openai

This is most visible after a stale or missing Codex app-server thread binding, because the fallback path is then used for compaction.

Root Cause

This is most visible after a stale or missing Codex app-server thread binding, because the fallback path is then used for compaction.

Fix Action

Fixed

Code Example

codex app-server compaction could not use thread binding
...
No_API_key_found_for_provider_openai

---

const selectedPiRuntimeProvider = resolveSelectedOpenAIPiRuntimeProvider({
  provider,
  harnessRuntime: agentHarness.id,
  agentHarnessId: agentHarness.id,
  authProfileProvider: params.authProfileId?.split(":", 1)[0],
  authProfileId: params.authProfileId,
  config: params.config,
  workspaceDir: resolvedWorkspace,
});

---

export function resolveEmbeddedCompactionTarget(params: {
  config?: OpenClawConfig;
  provider?: string | null;
  modelId?: string | null;
  authProfileId?: string | null;
  defaultProvider?: string;
  defaultModel?: string;
}): { provider: string | undefined; model: string | undefined; authProfileId: string | undefined } {
  const provider = params.provider?.trim() || params.defaultProvider;
  const model = params.modelId?.trim() || params.defaultModel;
  const override = params.config?.agents?.defaults?.compaction?.model?.trim();
  if (!override) {
    return {
      provider,
      model,
      authProfileId: params.authProfileId ?? undefined,
    };
  }
  // ...
}

---

import { resolveSelectedOpenAIPiRuntimeProvider } from "../openai-codex-routing.js";

export function resolveEmbeddedCompactionTarget(params: {
  config?: OpenClawConfig;
  provider?: string | null;
  modelId?: string | null;
  authProfileId?: string | null;
  defaultProvider?: string;
  defaultModel?: string;
}): { provider: string | undefined; model: string | undefined; authProfileId: string | undefined } {
  const provider = params.provider?.trim() || params.defaultProvider;
  const model = params.modelId?.trim() || params.defaultModel;
  const override = params.config?.agents?.defaults?.compaction?.model?.trim();
  if (!override) {
    const runtimeProvider = resolveSelectedOpenAIPiRuntimeProvider({
      provider: provider ?? "",
      harnessRuntime: "pi",
      authProfileProvider: params.authProfileId?.split(":", 1)[0],
      authProfileId: params.authProfileId ?? undefined,
      config: params.config,
    });
    return {
      provider: runtimeProvider || provider,
      model,
      authProfileId: params.authProfileId ?? undefined,
    };
  }
  // ...
}

---

it("routes OpenAI Codex OAuth compaction fallback through the Codex provider", () => {
  const result = buildEmbeddedCompactionRuntimeContext({
    workspaceDir: "/tmp/workspace",
    agentDir: "/tmp/agent",
    config: {
      auth: {
        order: {
          openai: ["openai-codex:work"],
          "openai-codex": ["openai-codex:work"],
        },
      },
      models: {
        providers: {
          openai: { agentRuntime: { id: "codex" } },
        },
      },
    } as OpenClawConfig,
    provider: "openai",
    modelId: "gpt-5.5",
    authProfileId: "openai-codex:work",
  });

  expect(result.provider).toBe("openai-codex");
  expect(result.model).toBe("gpt-5.5");
  expect(result.authProfileId).toBe("openai-codex:work");
});

---

npm test -- --run \
  src/agents/pi-embedded-runner/compaction-runtime-context.test.ts \
  src/agents/pi-embedded-runner/compact.hooks.test.ts \
  src/agents/command/cli-compaction.test.ts

---

Test Files  3 passed (3)
Tests       65 passed (65)
RAW_BUFFERClick to expand / collapse

Summary

Embedded Pi runs already normalize provider=openai plus an openai-codex:* auth profile to the openai-codex runtime provider. Embedded compaction fallback used a separate target resolver and did not apply the same routing rule.

That can produce an inconsistent compaction runtime context:

  • provider=openai
  • authProfileId=openai-codex:...
  • model=gpt-*

The main run path works, but the compaction/context-engine fallback can enter the plain OpenAI API-key path and fail with a missing OpenAI API key, even though the active session is using Codex OAuth.

Observed failure shape:

codex app-server compaction could not use thread binding
...
No_API_key_found_for_provider_openai

This is most visible after a stale or missing Codex app-server thread binding, because the fallback path is then used for compaction.

Affected path

Normal embedded run path already applies OpenAI/Codex runtime routing:

const selectedPiRuntimeProvider = resolveSelectedOpenAIPiRuntimeProvider({
  provider,
  harnessRuntime: agentHarness.id,
  agentHarnessId: agentHarness.id,
  authProfileProvider: params.authProfileId?.split(":", 1)[0],
  authProfileId: params.authProfileId,
  config: params.config,
  workspaceDir: resolvedWorkspace,
});

But the embedded compaction target resolver previously returned the caller provider directly when no explicit compaction model override was configured.

Old code:

export function resolveEmbeddedCompactionTarget(params: {
  config?: OpenClawConfig;
  provider?: string | null;
  modelId?: string | null;
  authProfileId?: string | null;
  defaultProvider?: string;
  defaultModel?: string;
}): { provider: string | undefined; model: string | undefined; authProfileId: string | undefined } {
  const provider = params.provider?.trim() || params.defaultProvider;
  const model = params.modelId?.trim() || params.defaultModel;
  const override = params.config?.agents?.defaults?.compaction?.model?.trim();
  if (!override) {
    return {
      provider,
      model,
      authProfileId: params.authProfileId ?? undefined,
    };
  }
  // ...
}

Suggested fix

Apply the same OpenAI/Codex Pi runtime-provider routing in the compaction target resolver.

New code:

import { resolveSelectedOpenAIPiRuntimeProvider } from "../openai-codex-routing.js";

export function resolveEmbeddedCompactionTarget(params: {
  config?: OpenClawConfig;
  provider?: string | null;
  modelId?: string | null;
  authProfileId?: string | null;
  defaultProvider?: string;
  defaultModel?: string;
}): { provider: string | undefined; model: string | undefined; authProfileId: string | undefined } {
  const provider = params.provider?.trim() || params.defaultProvider;
  const model = params.modelId?.trim() || params.defaultModel;
  const override = params.config?.agents?.defaults?.compaction?.model?.trim();
  if (!override) {
    const runtimeProvider = resolveSelectedOpenAIPiRuntimeProvider({
      provider: provider ?? "",
      harnessRuntime: "pi",
      authProfileProvider: params.authProfileId?.split(":", 1)[0],
      authProfileId: params.authProfileId ?? undefined,
      config: params.config,
    });
    return {
      provider: runtimeProvider || provider,
      model,
      authProfileId: params.authProfileId ?? undefined,
    };
  }
  // ...
}

Suggested regression test

Add a test to src/agents/pi-embedded-runner/compaction-runtime-context.test.ts:

it("routes OpenAI Codex OAuth compaction fallback through the Codex provider", () => {
  const result = buildEmbeddedCompactionRuntimeContext({
    workspaceDir: "/tmp/workspace",
    agentDir: "/tmp/agent",
    config: {
      auth: {
        order: {
          openai: ["openai-codex:work"],
          "openai-codex": ["openai-codex:work"],
        },
      },
      models: {
        providers: {
          openai: { agentRuntime: { id: "codex" } },
        },
      },
    } as OpenClawConfig,
    provider: "openai",
    modelId: "gpt-5.5",
    authProfileId: "openai-codex:work",
  });

  expect(result.provider).toBe("openai-codex");
  expect(result.model).toBe("gpt-5.5");
  expect(result.authProfileId).toBe("openai-codex:work");
});

Additional path review

Checked the other compaction/context paths:

  • compactEmbeddedPiSessionDirect calls resolveEmbeddedCompactionTarget.
  • queued/manual compactEmbeddedPiSession calls resolveEmbeddedCompactionTarget and buildEmbeddedCompactionRuntimeContext.
  • timeout/overflow compaction in run.ts calls buildEmbeddedCompactionRuntimeContext.
  • after-turn context-engine hooks call buildAfterTurnRuntimeContext, which calls buildEmbeddedCompactionRuntimeContext.
  • CLI-native harness compaction is a separate path. It does not forward the Codex OAuth profile into the context-engine fallback in the same way; provider selection is already based on the CLI/native harness session provider.

So the missing normalization appears localized to the embedded compaction target/runtime-context resolver, not a broader set of divergent fallback paths.

Verification

Focused tests:

npm test -- --run \
  src/agents/pi-embedded-runner/compaction-runtime-context.test.ts \
  src/agents/pi-embedded-runner/compact.hooks.test.ts \
  src/agents/command/cli-compaction.test.ts

Result:

Test Files  3 passed (3)
Tests       65 passed (65)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING