What version of the Codex App are you using?
26.317.21539 (1088) — bundled codex-cli 0.116.0-alpha.1
What subscription do you have?
ChatGPT Plus + Azure OpenAI (custom model_providers.azure in config.toml)
What platform is your computer?
Darwin 24.5.0 arm64
What issue are you seeing?
Resuming any past conversation from ChatGPT Plus while the current profile is set to Azure (or vice versa) produces:
{ "error": { "message": "The encrypted content gAAA...Sq-D could not be verified.
Reason: Encrypted content could not be decrypted or parsed.",
"type": "invalid_request_error", "param": null, "code": "invalid_encrypted_content" } }
Root cause: merge_persisted_resume_metadata() in codex_message_processor.rs restores model and reasoning_effort from ThreadMetadata but does not restore model_provider. So the conversation history (containing encrypted_content from the original provider) is sent to the wrong API endpoint, which cannot decrypt it.
The ThreadMetadata struct already stores model_provider (populated from threads.model_provider in state_5.sqlite), and ConfigOverrides.model_provider exists. It just is not wired up during resume.
Proposed fix
In codex-rs/app-server/src/codex_message_processor.rs, merge_persisted_resume_metadata:
fn merge_persisted_resume_metadata(
request_overrides: &mut Option<HashMap<String, serde_json::Value>>,
typesafe_overrides: &mut ConfigOverrides,
persisted_metadata: &ThreadMetadata,
) {
if has_model_resume_override(request_overrides.as_ref(), typesafe_overrides) {
return;
}
typesafe_overrides.model = persisted_metadata.model.clone();
+ typesafe_overrides.model_provider = Some(persisted_metadata.model_provider.clone());
if let Some(reasoning_effort) = persisted_metadata.reasoning_effort {
request_overrides.get_or_insert_with(HashMap::new).insert(
"model_reasoning_effort".to_string(),
serde_json::Value::String(reasoning_effort.to_string()),
);
}
}
This ensures the thread is resumed on the same provider that created it, regardless of the user's current active profile.
What steps can reproduce the bug?
- Configure both
profiles.plus (model_provider = "openai") and profiles.azure (model_provider = "azure") in ~/.codex/config.toml
- Create conversations using Plus profile
- Switch to Azure profile:
profile = "azure" in config.toml
- Open the Codex App and click on any previous Plus conversation → encrypted_content error
What is the expected behavior?
Resuming a conversation should automatically route to the original provider that created it. The model_provider is already persisted in threads.model_provider — it just needs to be applied during resume.
Additional information
Related: #11653 (encrypted content verification failures)
Related: #12998 (model picker empty for custom providers)
state_5.sqlite correctly stores the provider per thread — verified:
SELECT model_provider, count(*) FROM threads GROUP BY model_provider;
-- openai|922
-- azure|106
What version of the Codex App are you using?
26.317.21539 (1088) — bundled codex-cli 0.116.0-alpha.1
What subscription do you have?
ChatGPT Plus + Azure OpenAI (custom
model_providers.azurein config.toml)What platform is your computer?
Darwin 24.5.0 arm64
What issue are you seeing?
Resuming any past conversation from ChatGPT Plus while the current profile is set to Azure (or vice versa) produces:
Root cause:
merge_persisted_resume_metadata()incodex_message_processor.rsrestoresmodelandreasoning_effortfromThreadMetadatabut does not restoremodel_provider. So the conversation history (containingencrypted_contentfrom the original provider) is sent to the wrong API endpoint, which cannot decrypt it.The
ThreadMetadatastruct already storesmodel_provider(populated fromthreads.model_providerinstate_5.sqlite), andConfigOverrides.model_providerexists. It just is not wired up during resume.Proposed fix
In
codex-rs/app-server/src/codex_message_processor.rs,merge_persisted_resume_metadata:This ensures the thread is resumed on the same provider that created it, regardless of the user's current active profile.
What steps can reproduce the bug?
profiles.plus(model_provider = "openai") andprofiles.azure(model_provider = "azure") in~/.codex/config.tomlprofile = "azure"in config.tomlWhat is the expected behavior?
Resuming a conversation should automatically route to the original provider that created it. The
model_provideris already persisted inthreads.model_provider— it just needs to be applied during resume.Additional information
Related: #11653 (encrypted content verification failures)
Related: #12998 (model picker empty for custom providers)
state_5.sqlitecorrectly stores the provider per thread — verified: