find_model_by_namespaced_suffix rejects hyphens in namespace, breaking custom provider slugs like llm-gateway/gpt-5.4

Open 💬 3 comments Opened Mar 11, 2026 by kapral18
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of Codex CLI is running?

0.113.0

What subscription do you have?

Plus

Which model were you using?

llm-gateway/gpt-5.4 (via custom model_providers.litellm)

What platform is your computer?

Darwin 25.3.0 arm64 arm

What terminal emulator and version are you using (if applicable)?

Ghostty 1.6.2 + tmux 3.5a

What issue are you seeing?

find_model_by_namespaced_suffix in codex-rs/core/src/models_manager/manager.rs rejects any namespace containing a hyphen (-). Model slugs like llm-gateway/gpt-5.4 cannot resolve to the bundled gpt-5.4 metadata, even though the suffix matches a known model.

The check at L195-L199:

if !namespace
    .chars()
    .all(|c| c.is_ascii_alphanumeric() || c == '_')
{
    return None;
}

Only allows [a-zA-Z0-9_] in the namespace. Hyphens are common in provider/proxy prefixes (e.g. llm-gateway/, my-proxy/).

Codex falls back to degraded metadata and emits:

⚠ Model metadata for llm-gateway/gpt-5.4 not found. Defaulting to fallback metadata; this can degrade performance and cause issues.

The only workaround is providing a full model_catalog_json that clones the entire bundled catalog (~230 KB) just to add one alias entry.

What steps can reproduce the bug?

  1. Configure a custom model provider in config.toml:
model = "llm-gateway/gpt-5.4"
model_provider = "litellm"

[model_providers.litellm]
name = "LiteLLM"
base_url = "https://your-litellm-proxy/v1"
env_key = "LITELLM_PROXY_KEY"
wire_api = "responses"
  1. Start Codex.
  2. Observe the fallback metadata warning.

The namespace llm-gateway contains a hyphen which fails the is_ascii_alphanumeric() || c == '_' check in find_model_by_namespaced_suffix, so the suffix gpt-5.4 is never looked up in the bundled catalog.

What is the expected behavior?

llm-gateway/gpt-5.4 should resolve to the bundled gpt-5.4 model metadata via namespace stripping, the same way custom_provider/gpt-5.4 (underscore namespace) already works.

Suggested fix: allow - in the namespace character set:

.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')

Additional information

  • Related: #12380 (overriding model metadata) — different ask but same pain point area.
  • The llm-gateway/ prefix is a common LiteLLM proxy convention and cannot be changed on the proxy side.
  • Current workaround: provide a full model_catalog_json that clones the entire bundled catalog (~230 KB, 12 models) just to add one alias entry — fragile and depends on models_cache.json (a runtime artifact).

View original on GitHub ↗

3 Comments

github-actions[bot] contributor · 5 months ago

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

  • #12898

Powered by Codex Action

edenfunf · 5 months ago

Root Cause Confirmed

The check lives in codex-rs/core/src/models_manager/manager.rs, function find_model_by_namespaced_suffix (around line 195):

if !namespace
    .chars()
    .all(|c| c.is_ascii_alphanumeric() || c == '_')
{
    return None;
}

The namespace segment in llm-gateway/gpt-5.4 contains a hyphen, so the guard returns None immediately. The function never reaches find_model_by_longest_prefix(suffix, candidates), and the caller falls back to degraded metadata.

Fix

Add || c == '-' to the allowed character set and update the doc comment:

/// This only strips one leading namespace segment and only when the namespace is ASCII
/// alphanumeric, underscore, or hyphen to avoid broadly matching arbitrary aliases.
/// Hyphens are allowed because common proxy/gateway prefixes (e.g. `llm-gateway/`)
/// contain them.
fn find_model_by_namespaced_suffix(model: &str, candidates: &[ModelInfo]) -> Option<ModelInfo> {
    let (namespace, suffix) = model.split_once('/')?;
    if suffix.contains('/') {
        return None;
    }
    if !namespace
        .chars()
        .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
    {
        return None;
    }
    Self::find_model_by_longest_prefix(suffix, candidates)
}

The change is a single character addition and doesn't touch any public API. I also added a test get_model_info_matches_hyphenated_namespace_suffix alongside the existing get_model_info_matches_namespaced_suffix test in manager_tests.rs — all 19 tests in the models_manager suite pass.

Happy to open a PR if that would be useful.

JaysonGeng · 4 months ago

Implemented this on fix/hyphenated-model-namespaces and pushed it to my fork.

Change summary:

  • allow - alongside alphanumeric / _ in find_model_by_namespaced_suffix
  • update the helper doc comment
  • add regression test get_model_info_matches_hyphenated_namespaced_suffix

Local validation:

  • just fix -p codex-core ✅
  • just fmt ✅
  • git diff --check ✅
  • cargo test -p codex-core get_model_info_matches_hyphenated_namespaced_suffix -- --exact hit the repo's 60s local hard timeout on this machine after compiling, but no test failure was surfaced
  • just argument-comment-lint is blocked locally because this machine does not have bazel available for that recipe

openai/codex is still invitation-only from this account, so I can't open the upstream PR directly. If useful, a maintainer can invite a PR from the branch above or cherry-pick 6219e89.