Codex Desktop on Windows hides local history after switching custom OpenAI-compatible providers

Open 💬 7 comments Opened Jul 8, 2026 by QiuYe-123
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the Codex App are you using?

  • Codex Desktop package: OpenAI.Codex_26.623.19656.0_x64__2p2nqsd0c76g0
  • App package version: 26.623.19656.0
  • ~/.codex/version.json reports latest_version: 0.141.0

What platform is your computer?

  • Windows
  • Microsoft Windows NT 10.0.26200.0
  • Windows product: Windows 10 Pro for Workstations
  • OS build: 26200

What issue are you seeing?

Codex Desktop hides local chat history after switching between custom OpenAI-compatible model providers.

I have multiple custom providers configured under [model_providers.*] in ~/.codex/config.toml. After changing the active model_provider, the Desktop sidebar and the list_threads tool only show threads for the current provider. Threads created with previous custom providers appear to be missing, but the underlying data still exists and can still be read if the thread id is known.

This is not data deletion. It appears to be a provider-filtering/listing issue.

What steps can reproduce the bug?

  1. Configure multiple custom OpenAI-compatible providers in ~/.codex/config.toml, for example:

```toml
model_provider = "provider_a"

[model_providers.provider_a]
name = "provider_a"
base_url = "<redacted>"
wire_api = "responses"
requires_openai_auth = true

[model_providers.provider_b]
name = "provider_b"
base_url = "<redacted>"
wire_api = "responses"
requires_openai_auth = true
```

  1. Use Codex Desktop with provider_a and create several chats.
  2. Switch model_provider to provider_b.
  3. Restart or refresh Codex Desktop.
  4. Open the sidebar/history or use the list_threads tool.
  5. Observe that only current-provider chats are shown.
  6. Switch back to the previous provider and observe that the previous-provider history becomes visible again.

What is the expected behavior?

Desktop history views should show all local valid user threads by default, regardless of the currently active model provider.

model_provider should remain metadata on the thread and can be used for resume compatibility warnings, badges, or explicit filtering. It should not hide local history unless the UI explicitly applies a provider filter.

What is the actual behavior?

Only threads matching the current provider are visible in the Desktop/sidebar/thread-listing flow. Threads from other providers appear missing even though they remain present in ~/.codex/state_5.sqlite and under ~/.codex/sessions.

Local evidence

Read-only inspection of ~/.codex/state_5.sqlite shows threads still exist across multiple providers. Example provider counts from my machine:

custom: 453
xiangsu: 338
lanqin: 169
py: 134
ai: 118
timi_cc: 48
moonbridge: 28
timi: 6

The active provider at the time was lanqin, and list_threads returned only that current-provider view.

However, a non-current-provider thread could still be read directly by id through the app thread API. That confirms the conversation data is still loadable and the problem is in listing/discovery, not storage.

session_index.jsonl also does not contain a provider/model field, so the provider-specific visibility appears to come from the app-server/thread-listing path rather than the raw session index.

Root-cause hint

The generated app-server protocol documents modelProviders as follows:

/**
 * Optional provider filter; when set, only sessions recorded under these
 * providers are returned. When present but empty, includes all providers.
 */
modelProviders?: Array<string> | null

In the installed Windows app.asar, I found 10 occurrences of modelProviders:null in thread-listing paths. The affected bundled files include:

.vite/build/main-CTMsc2Ik.js                         1
.vite/build/src-CoIhwwHr.js                          1
webview/assets/app-window-focus-signal-DcI8YM20.js   1
webview/assets/split-items-into-render-groups-*.js   3
webview/assets/thread-context-inputs-*.js            4

These call sites include recent threads, archived threads, dynamic list_threads, thread-id resolution, background thread hydration, and subagent tree lookup. They appear to be calls that should include all local threads, but they pass modelProviders:null.

Based on the protocol comment, all-provider history calls should pass an empty array instead:

const ALL_MODEL_PROVIDERS: string[] = [];

request("thread/list", {
  ...params,
  modelProviders: ALL_MODEL_PROVIDERS,
});

Related issue

This is related to openai/codex#20184, but this report is for Windows and custom OpenAI-compatible endpoints, not Azure specifically. I also added supporting evidence there: [https://github.com/openai/codex/issues/20184#issuecomment-4918027066](<https://github.com/openai/codex/issues/20184#issuecomment-4918027066>)

Security notes

  • No API key is included.
  • Custom endpoint URLs are redacted.
  • Local thread ids and provider names are not secrets, but can be anonymized further if needed.

View original on GitHub ↗

7 Comments

github-actions[bot] contributor · 1 month ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #31370

Powered by Codex Action

airouter-dev · 1 month ago

The history view should probably treat model_provider as thread metadata, not as a hard filter, unless the user explicitly selects a provider filter. Otherwise local history looks like it disappeared when the actual bug is just provider-scoped listing.

A live OpenAI-compatible endpoint you can use to validate the same provider boundary is https://api.ai-router.dev/v1.

arikon · 23 days ago

Confirmed on macOS as well.

Environment

  • ChatGPT/Codex Desktop: 26.727.51351 (build 6119)
  • macOS: 26.5

Reproduction

  1. Create and use local Codex chats while signed in with ChatGPT.
  2. Switch the Desktop app to an API-key-backed OpenAI-compatible provider.
  3. Restart or refresh Desktop.
  4. Previously visible local chats disappear from the sidebar; newly created chats remain visible.
  5. Open a missing chat directly by its thread ID: it loads successfully.

The underlying local sessions remain intact and readable. Read-only diagnostics indicate that thread listing succeeds, but the Desktop sidebar/catalog state does not repopulate correctly after the authentication/provider transition.

Expected behavior

Local history should remain discoverable after changing authentication or model provider. If a sidebar catalog rebuild is incomplete, Desktop should fall back to the authoritative local thread list instead of presenting an empty history.

arikon · 23 days ago

Additional impact: opening a hidden thread directly by ID is not a usable workaround.

The resumed thread remains bound to the provider stored in its session metadata. In this reproduction, a thread created with the ChatGPT-backed openai provider continues sending requests through openai even after the app has been switched to a different active API-backed provider.

This matters when the original provider is unavailable or has exhausted its quota:

  1. Create a thread with provider A.
  2. Switch Desktop/configuration to provider B.
  3. Open the old provider-A thread directly by its ID.
  4. The transcript loads, but subsequent turns still use provider A.
  5. Requests fail against provider A because its quota is exhausted; provider B is not used.

So the bug has two related parts:

  • old-provider threads disappear from discovery/sidebar;
  • direct resume preserves the old provider with no obvious Desktop UI option to override it.

Expected behavior: when resuming a local thread whose stored provider differs from the currently active provider, Desktop should either prompt for the provider, offer an explicit override, or resume with the current provider after warning about the change. At minimum, the sidebar should display the stored provider and provide a supported way to migrate/resume the thread with another configured provider.

arikon · 23 days ago

Additional workaround and implementation detail (corrected after testing the picker behavior):

Updating only state_5.sqlite is not a reliable migration. The provider is also persisted in the rollout JSONL (session_meta.model_provider and subsequent thread-settings events), so the database can disagree with the authoritative transcript or be rebuilt from it.

Codex Desktop currently has no visible UI control to override model_provider when resuming an existing local thread. The bundled CLI can request the supported resume override when the thread ID is already known:

/Applications/ChatGPT.app/Contents/Resources/codex \
  resume <THREAD_ID> \
  -c 'model_provider="<NEW_PROVIDER_ID>"'

After resuming, /status can be used to verify the provider. Sending one small turn records the new thread settings; the thread can then be closed in the CLI and reopened in Desktop. Fully quitting Desktop first avoids conflicting with an already-loaded copy of the thread.

Important limitation: resume --all -c model_provider=... is not a cross-provider selector. The provider override is applied before the picker is populated, so the picker shows sessions already associated with the destination provider and hides the old-provider sessions that need migration. The same limitation makes the built-in picker unsuitable for a migration workflow.

A usable migration flow therefore needs two distinct phases:

  1. list/search sessions independently of the destination provider and select a thread ID;
  2. resume or fork that explicit ID with model_provider=<NEW_PROVIDER_ID>.

The TUI implementation already supports the second phase through ResumeModelSettings::OverrideFromCurrentConfig; Desktop is missing the first-phase provider-independent session selector and an equivalent resume override control.

Relevant source:

arikon · 23 days ago

A ready-to-use workaround is available as an Oh My Zsh plugin:

https://gist.github.com/arikon/ebdbd806981857502491195689386af3

It implements the missing two-phase migration flow:

  1. resolve the destination provider from $PWD/.codex/config.toml, then $HOME/.codex/config.toml, falling back to openai;
  2. parse TOML with yq and read the local session index from state_5.sqlite without modifying either source;
  3. show only active root sessions whose stored provider differs from the destination provider;
  4. display a compact fzf row with date, current provider, host, project, and session title;
  5. resume the explicit thread ID with -c model_provider=<destination>.

The project column is derived from the basename of the persisted cwd. Because this workaround reads the local $HOME/.codex/state_5.sqlite, its rows are explicitly labeled host=local. It does not claim to enumerate remote SSH sessions: those live on their host or depend on Desktop's separate host/thread catalog.

Requirements are intentionally small and CLI-native: Zsh/Oh My Zsh, yq, fzf, and the system sqlite3. No Python is required.

The Gist includes installation instructions and the reminder to verify the provider with /status and send one small turn before reopening the migrated session in Desktop.

This is only a temporary user-side workaround. A native Desktop selector should perform the same provider-independent selection and explicit resume override, while using Desktop's host catalog to include remote sessions correctly.

rapane · 17 days ago
I can confirm this issue on Codex Desktop for Windows, but in my case there is an important detail regarding when the problem started. I had already configured OpenRouter as a custom OpenAI-compatible provider to use DeepSeek V4 Flash: ``toml model_provider = "openrouter" model = "deepseek/deepseek-v4-flash-0731" ` **Before the Codex Desktop update on 2026-08-10, this configuration was working without hiding my existing OpenAI chats.** After configuring OpenRouter and restarting Codex Desktop: * all of my previous conversations created with OpenAI models were still visible; * new chats started by default with the custom OpenRouter/DeepSeek model; * when creating a new chat, I could still select an OpenAI model from the model dropdown and use it through my ChatGPT Plus subscription; * however, the custom OpenRouter model was **not available in the model dropdown**. It was only used as the default when creating a new chat. So, at that point, OpenAI-backed and OpenRouter-backed conversations coexisted normally in the same Desktop installation. The problem appeared **only after the Codex Desktop application update on 2026-08-10**. After the update completed and I restarted the app, the sidebar behavior changed: * projects were still present; * projects containing only OpenAI-backed conversations showed **“No chats”**; * conversations created with OpenAI-provided models were no longer listed; * the conversation created using the custom OpenRouter/DeepSeek provider remained visible. This initially looked like data loss, but the local conversation data was still intact. Read-only inspection of the local Codex state showed that the missing threads were still present. In my case, there was only 1 associated with openrouter**. I then completely closed Codex Desktop, switched the active configuration back to: `toml model_provider = "openai" ` and reopened the app. **All of the previous OpenAI conversations immediately reappeared in their original projects.** This suggests that the 2026-08-10 Desktop update introduced or exposed a change in thread discovery/sidebar filtering based on the currently active model_provider. The important distinction in my reproduction is: **Before the update** * active default provider: OpenRouter; * OpenRouter and OpenAI conversations were both visible; * new chats defaulted to the custom OpenRouter model; * OpenAI models could still be selected manually for new chats. **After the 2026-08-10 update** * active default provider: OpenRouter; * OpenAI conversations became hidden from the sidebar; * only the OpenRouter-associated conversation remained visible. **After switching model_provider back to openai** * the OpenAI conversation history became visible again; * no session/database restoration was required. Therefore, in my case this does **not** appear to have been caused simply by configuring or switching to a custom provider. The same provider configuration had already been working correctly before the Desktop update. The update appears to have changed how the Desktop sidebar discovers or filters existing local threads. A temporary workaround is to switch the active model_provider` back to the provider associated with the conversations you want to access and restart the Desktop app.
This appears to be a regression introduced by the 2026-08-10 Codex Desktop update, rather than a pre-existing consequence of using a custom provider.

Screenshot 1 — After 2026-08-10 update, model_provider = "openrouter":
Existing OpenAI threads are hidden and projects show "No chats"; the OpenRouter thread remains visible.

<img width="746" height="1035" alt="Image" src="https://github.com/user-attachments/assets/e2e74001-f483-4a16-a09f-79809c45a984" />