Client or integration
OpenCodex dashboard
Area
Catalog / models
Summary
candidateCapabilityEvidence() in src/routing/capability.ts reads the cached Codex catalog to source routing evidence, but its row filter expects field names the catalog writer never produces. Every row is discarded, so the catalog branch of the evidence chain is dead code.
cachedCatalogModels() filters on model.id and model.provider and then reads model.contextWindow / model.inputModalities. The on-disk catalog writes slug (a combined provider/id), context_window, and input_modalities. None of the four expected keys exist on any row.
This is not specific to one provider. On a live install with 17 catalog rows, zero survive the filter — native OpenAI rows, anthropic/claude-opus-5, xai/grok-4.6, all of them.
The defect is invisible for most providers because the catalog is the fourth fallback: a provider that declares modelContextWindows / modelInputModalities inline is answered by an earlier branch. It becomes visible for a model registered through ocx models add, whose capability metadata lives only in the catalog. Such a model routes as image-blind and context-unknown even though the CLI, the config, and the catalog file all show the correct values.
Reproduction
-
Register a custom model whose provider block declares no modelContextWindows and no modelInputModalities:
ocx models add <provider> <model> --context-window 262144 --modalities text,image
-
Confirm the value reached the catalog file (~/.codex/opencodex-catalog.json):
{ "slug": "<provider>/<model>", "context_window": 262144,
"input_modalities": ["text", "image"] }
-
Assemble routing evidence for that candidate:
candidateCapabilityEvidence(config, "<provider>", "<model>")
=> { tools: true, serviceTier: "unsupported", encryptedCodexTasks: false }
contextWindow and image are both absent.
-
Count survivors directly:
TOTAL: 17 | SURVIVING capability.ts filter: 0
Version
2.21.0
Operating system
macOS 26 (arm64)
Provider and model
Reproduced with a local llama.cpp provider (openai-chat adapter) and a custom-registered model; the 17-to-0 count covers anthropic, xai, google-antigravity, kimi, alibaba-token-plan-intl and native OpenAI rows equally.
Logs or error output
KEYS: slug,display_name,description,default_reasoning_level,supported_reasoning_levels,
shell_type,visibility,supported_in_api,priority,...,input_modalities,...,context_window,
max_context_window,auto_compact_token_limit,...,opencodex_catalog_kind
provider? undefined id? undefined context_window? 262144
Screenshots and supporting files
Not applicable — reproduced through direct function calls.
Redacted configuration
{ "providers": { "<provider>": { "adapter": "openai-chat",
"baseUrl": "http://<host>:<port>/v1", "defaultModel": "<model>" } },
"customModels": [ { "provider": "<provider>", "modelId": "<model>",
"contextWindow": 262144, "inputModalities": ["text", "image"] } ] }
Checks
Note on the naive fix
Reading context_window / input_modalities straight off the row is not a correct repair. ensureStrictCatalogFields() synthesizes both fields for Codex's strict parser (src/codex/catalog/parsing.ts:315 writes ["text"], :328 writes 128000), so their presence cannot distinguish a real provider assertion from a placeholder. Reading them would turn unknown into a confident image: false and a fabricated 128000, violating the module's own "unknown is not zero" contract. A correct fix needs a separate provenance channel that records only what a provider actually asserted.
Client or integration
OpenCodex dashboard
Area
Catalog / models
Summary
candidateCapabilityEvidence()insrc/routing/capability.tsreads the cached Codex catalog to source routing evidence, but its row filter expects field names the catalog writer never produces. Every row is discarded, so the catalog branch of the evidence chain is dead code.cachedCatalogModels()filters onmodel.idandmodel.providerand then readsmodel.contextWindow/model.inputModalities. The on-disk catalog writesslug(a combinedprovider/id),context_window, andinput_modalities. None of the four expected keys exist on any row.This is not specific to one provider. On a live install with 17 catalog rows, zero survive the filter — native OpenAI rows,
anthropic/claude-opus-5,xai/grok-4.6, all of them.The defect is invisible for most providers because the catalog is the fourth fallback: a provider that declares
modelContextWindows/modelInputModalitiesinline is answered by an earlier branch. It becomes visible for a model registered throughocx models add, whose capability metadata lives only in the catalog. Such a model routes as image-blind and context-unknown even though the CLI, the config, and the catalog file all show the correct values.Reproduction
Register a custom model whose provider block declares no
modelContextWindowsand nomodelInputModalities:Confirm the value reached the catalog file (
~/.codex/opencodex-catalog.json):{ "slug": "<provider>/<model>", "context_window": 262144, "input_modalities": ["text", "image"] }Assemble routing evidence for that candidate:
contextWindowandimageare both absent.Count survivors directly:
Version
2.21.0
Operating system
macOS 26 (arm64)
Provider and model
Reproduced with a local llama.cpp provider (
openai-chatadapter) and a custom-registered model; the 17-to-0 count covers anthropic, xai, google-antigravity, kimi, alibaba-token-plan-intl and native OpenAI rows equally.Logs or error output
Screenshots and supporting files
Not applicable — reproduced through direct function calls.
Redacted configuration
{ "providers": { "<provider>": { "adapter": "openai-chat", "baseUrl": "http://<host>:<port>/v1", "defaultModel": "<model>" } }, "customModels": [ { "provider": "<provider>", "modelId": "<model>", "contextWindow": 262144, "inputModalities": ["text", "image"] } ] }Checks
Note on the naive fix
Reading
context_window/input_modalitiesstraight off the row is not a correct repair.ensureStrictCatalogFields()synthesizes both fields for Codex's strict parser (src/codex/catalog/parsing.ts:315writes["text"],:328writes128000), so their presence cannot distinguish a real provider assertion from a placeholder. Reading them would turn unknown into a confidentimage: falseand a fabricated128000, violating the module's own "unknown is not zero" contract. A correct fix needs a separate provenance channel that records only what a provider actually asserted.