Repository navigation
[Bug][2.36.0] Live discovery drops a callable configured model: isDatedVariantId only matches YYYYMMDD and only folds base->dated #3024
Description
Activity
- addedbugSomething isn't workingSomething isn't workingcatalogModel catalog, slugs, visibility, routed entriesModel catalog, slugs, visibility, routed entries
on Aug 30, 2026 리뷰 · 우선순위 66 / 80
이 이슈가 말하는 구멍은 지금
devHEAD870a2adb6에도 그대로 있다. 패키지 버전은2.37.0이다. 작성자가 본 설치본은2.36.0이다. 그 사이 올라간 것은 버전 줄(#3013)이지, 날짜 접미사 접기 수리가 아니다. 라이브 디스커버리가 설정에 있는 호출 가능한 모델 id 를 카탈로그에서 조용히 빼는 길이다.증상은 이렇다.
alibaba-token-plan-intl설정에deepseek-v4-pro-0813이 들어 있다. 업스트림/models는 베이스 iddeepseek-v4-pro만 광고한다. 같은 모델은 업스트림에도, 프록시 네임스페이스로도 HTTP 200 으로 호출된다. 그런데ocx models live와GET /api/models에서는 그 날짜 id 가 사라진다.GET /api/providers의discovery.status는 그냥 ok 다. 대시보드와 Codex picker 는 죽은 모델처럼 그린다. 라우팅 구멍은 아니다. 카탈로그 투영 구멍이다.접기 함수가 한쪽으로만 본다.
isDatedVariantId(liveId, configuredId)는 live 가 configured 에-YYYYMMDD를 붙인 경우만 참이다. 설정이 날짜 id 이고 라이브가 베이스 id 인 반대 방향은startsWith에서 바로 거짓이다. 설정을 라이브에 대입해도 접미사0813은 8자리가 아니라서/^\d{8}$/가 거절한다.mergeConfiguredModelsIntoLiveCatalog는 그 함수가 참일 때만 설정 id 를 라이브 행 위에 남긴다. 실패하면shouldRetainConfiguredProviderModel로 넘어가는데, 그 탈출구는 호환 허용 목록과 opencode-free 뿐이다. alibaba Token Plan 은 없다. 그래서 설정 id 는droppedConfiguredIds로 떨어진다.이건 #1690 이 아니다. #1690 은 업스트림이 진짜로 안 주는 행을 남기는
retainModels옵션이다. 메인테이너 리뷰가 이미 말했다. 무조건 남기면 죽은 행이 살아난다. 이 이슈는 라이브에 대응 행이 있을 때만 접는 기존 길을 고치자는 쪽이다. 초안 PR #2860 과 #2122 는 #1690 구현이다. 이 구멍의 수리로 합치지 마라. 그 PR 들은 types/config 분할에도 걸릴 수 있다. 미리보기 배포는 계획에 없다. 이 접기만 고치는 열린 PR 은 없다.같은 시각의 #3023 과도 다르다. #3023 은 네이티브 GPT-5.6 관리면 캐시다. 이 이슈는 라우트 공급자 라이브 병합이다. 작성자가 같은 kaicot 이라도 한 이슈로 묶지 마라.
라인 src/codex/catalog/provider-fetch.ts 938 -
isDatedVariantId가 live 가 configured-YYYYMMDD 일 때만 참이다. 4자리 MMDD 도, 설정=날짜/라이브=베이스 반대 방향도 거짓이다.
라인 src/codex/catalog/provider-fetch.ts 1671 - 병합 루프가 그 함수 한 방향으로만 접는다. 실패하면 설정 id 는 드롭 목록으로 간다.
라인 src/codex/catalog/provider-fetch.ts 1631 -shouldRetainConfiguredProviderModel탈출구는 호환 허용 목록과 opencode-free 뿐이다. alibaba-token-plan-intl 은 없다.
경로 src/codex/catalog/provider-fetch.ts CALLABLE_CONFIGURED_COMPATIBILITY_MODELS - kimi/xai 쪽 별칭 잔류용이다. Token Plan 날짜 id 를 살리는 칸이 아니다.
경로 GET /api/providers discovery.status - 한 번 경고는 콘솔에 나가도 API 는 ok 라서 대시보드가 드롭을 성공으로 읽는다.메인테이너의 판단이 필요한 지점
- 접미사를 YYYYMMDD 만 둘지, 작성자 제안처럼 달력형 MMDD 도 받을지.
-4096같은 숫자 접미사를 날짜로 오인하지 않게 달력 가드가 필요하다 - 접기를 대칭으로 만들 때 라이브 베이스 행과 설정 날짜 행을 둘 다 남길지, 하나로 접을지. 작성자 플랜 콘솔은 둘을 다른 상품으로 적는다
discovery.status를 드롭이 있으면 실패/부분으로 바꿀지, 콘솔 경고만 둘지- [Feature]: config-level retainModels allow-list so operators keep configured models in the authoritative live catalog #1690 retainModels 기차(feat(catalog): honor per-provider retainModels opt-in (closes #1690) #2860/feat(catalog): config-level retainModels allowlist for authoritative discovery (#1690) #2122)와 이 접기를 한 PR 에 넣을지. 넣지 않는 쪽이 안전하다
너의 추천
열어 둔다. 라벨을 바꾸지 마라. #1690 중복으로 닫지 마라. #3023 과도 묶지 마라. 초안 #2860 / #2122 를 rebase 해서 이 이슈를 덮지 마라. 고치는 자리는isDatedVariantId와 그 한 줄 호출이다. 라이브에 대응 행이 있을 때만 설정 id 를 남기는 조건을 유지하라. 죽은 행을 부활시키지 마라. 좁은 단위 테스트(8자리, 4자리, 반대 방향,-4096비날짜)를 붙인 작은 PR 로dev에 올려라.이 댓글은 grok-bot이 작성했습니다
- 접미사를 YYYYMMDD 만 둘지, 작성자 제안처럼 달력형 MMDD 도 받을지.
Follow-up on my own suggested fix: it is incomplete — please don't merge the regex from the issue body as written. I validated it against real model ids and it only covers a subset of providers.
I enumerated every numeric-suffixed id pair among the 378 model ids this install exposes via
GET /api/models. Caveat on how to read this: the fold only ever compares configured vs live ids within one provider, so these pairs are format-prevalence data, not a list of actual fold candidates. What they do show is which suffix conventions are in use in the wild.The current
/^\d{8}$/rule matches 0 of the 26 numeric-suffixed pairs on this install.Suffix format Example \d{8}(current)My MMDD proposal Combined YYYYMMDD(none observed here) yes yes yes YYMMDDsolar-pro4-260806,syn-pro-251021no no yes MMDDdeepseek-v4-pro-0813,kimi-k2-0905,gpt-3.5-turbo-0613no yes yes YYMMmistral-large-2407,qwen3-235b-a22b-2507no no yes YYYY-MM-DDgpt-4o-2024-08-06,qwen-plus-2025-07-28no no no MM-DDgemini-2.5-pro-preview-05-06no no no So the MMDD-only patch I proposed would fix DeepSeek/Kimi/OpenAI-style ids and leave Mistral, Qwen, and Solar/Syn silently broken.
Revised numeric rule — 4/6/8 digits, still strict on month/day so version numbers and context sizes are not swallowed:
const MM = String.raw`(?:0[1-9]|1[0-2])`; const DD = String.raw`(?:0[1-9]|[12]\d|3[01])`; // YYYYMMDD | YYMMDD | MMDD | YYMM const DATE_SUFFIX = new RegExp( String.raw`^(?:\d{4}${MM}${DD}|\d\d${MM}${DD}|${MM}${DD}|\d\d${MM})$`, );
4-digit
MMDDandYYMMbarely overlap in validity (0813is only valid as MMDD,2407only as YYMM), so accepting either is unambiguous enough in practice, and both readings mean "dated release" anyway.Against the same 378 ids this rule matches 13 of 26 pairs. The 13 it rejects are all genuinely non-dates or out of scope:
rejected, correctly: 5 mistralai/mistral-medium-3-5 version number 16k openai/gpt-3.5-turbo-16k context size 30b-a3b-instruct qwen/qwen3-coder-30b-a3b-instruct variant name 0813:batch deepseek/deepseek-v4-pro-0813:batch batch lane of an already-dated id 0731:batch deepseek/deepseek-v4-flash-0731:batch 2512:batch mistralai/mistral-large-2512:batch 5:batch mistralai/mistral-medium-3-5:batch rejected, but arguably should match (hyphenated ISO -- separate decision for you): 2024-05-13 openai/gpt-4o-2024-05-13 2024-08-06 openai/gpt-4o-2024-08-06 2024-11-20 openai/gpt-4o-2024-11-20 2024-07-18 openai/gpt-4o-mini-2024-07-18 2025-07-28 qwen/qwen-plus-2025-07-28 05-06 google/gemini-2.5-pro-preview-05-06One caveat I should state plainly. The bidirectional half of my proposal has exactly the downside the #1690 review warned about. With
configured = dated,live = base, a deprecated snapshot left in someone's config (saymistral-large-2407after upstream retires it) would now be retained instead of dropped. For the model in this issue I verified callability directly (the two HTTP 200s in the issue body), but the fold cannot check that in general.If that trade-off is not acceptable, there is a narrower scope: keep the fold one-directional and only widen the suffix format. That does not fix
deepseek-v4-pro-0813— the case that opened this issue — but it does fix thebase -> dateddirection for Mistral, Qwen, Solar and Syn, which is currently dead for every format observed here.Happy to send a PR against
devwith table-driven tests over all six formats above. Just tell me which scope you want:- widen suffix format only (one-directional, no resurrection risk), or
- widen format + make the fold bidirectional (fixes this issue, accepts the deprecated-snapshot risk).
Thank you for withdrawing your own patch with data instead of letting it get merged. That saved a bad fold from shipping, and the format table is the most useful thing in this thread.
I verified your revised regex against the values in your table plus the ones I care about, and I am picking scope 1: widen the suffix format, keep the fold one-directional — with one change to your pattern.
Where your revised rule needs an edit.
MMDDandYYMMboth being 4 digits is worse than "barely overlap". Two concrete collisions:1024 → matches (reads as Oct 24) 1301 → matches (reads as YYMM: year 13, month 01)1024is the one that matters. Context sizes appear as bare numeric suffixes in real ids,-1024is a plausible one, and no month/day tightening can exclude it because October 24th is a real date.1301is excludable — restrictingYYto2\ddrops it while keeping every observed id (2407,2507,2512,260806,251021):const YY = String.raw`(?:2\d)`; // 2020s, not any two digits const DATE_SUFFIX = new RegExp( String.raw`^(?:\d{4}${MM}${DD}|${YY}${MM}${DD}|${MM}${DD}|${YY}${MM})$`, );
I checked
2048,4096,8192are all rejected by both versions; only1024survives, and that one is irreducible. I would rather accept a rare false positive on a 4-digit context suffix than leave Mistral/Qwen/Solar/Syn dead, but it should be a known cost written into the test table, not a surprise later.Why one-directional. The resurrection risk you named is the deciding factor.
isDatedVariantIdexists so a configured base id can inherit a live dated row's metadata — the configured id is what the operator asked for and what gets served. Reversing it means a configured id the provider no longer lists gets retained on the strength of a format match, and the fold has no callability signal to check that with. You verifieddeepseek-v4-pro-0813by hand with two 200s; the code cannot. #1690 is the right place for "operator insists on keeping this id" because it is an explicit allow-list, not an inference.So
deepseek-v4-pro-0813stays unfixed by this change, and that is a deliberate call rather than an oversight. The honest framing: your issue found two defects, and I am fixing the one that can be fixed safely.PR scope, if you still want to send it. Please do:
isDatedVariantIdatsrc/codex/catalog/provider-fetch.ts:938— widen the numeric suffix, keep directionconfigured=base → live=dated- table-driven tests over all six formats in your table, including the rejections, and an explicit case documenting that
1024is accepted as MMDD - extend the existing case at
tests/codex-catalog.test.ts:3504rather than adding a parallel block - no change to
shouldRetainConfiguredProviderModelor theretainModelspath
Target
dev. Hyphenated ISO (gpt-4o-2024-08-06,gemini-2.5-pro-preview-05-06) is out of scope here — a hyphenated suffix is ambiguous against ordinary name segments and needs its own decision.Priority stays 66/80. Still open, now with an agreed shape.
- added a commit that references this issue
on Aug 31, 2026 Automated translation bookkeeping — detected language: English.
- added a commit that references this issue
on Sep 14, 2026 - added a commit that references this issue
on Sep 17, 2026
Client or integration
Direct HTTP/API client (also visible in the OpenCodex dashboard and
ocx models live)Area
Catalog / models
Summary
mergeConfiguredModelsIntoLiveCatalog()has a dated-alias folding path whose job is to keep a configured model id in the catalog when live discovery returns the same model under a different, date-suffixed id. That fold is implemented byisDatedVariantId(), which is wrong in two independent ways:YYYYMMDDsuffix. Providers that use a 4-digitMMDDsuffix (Alibaba Token Plan / DeepSeek:deepseek-v4-pro-0813,deepseek-v4-flash-0731) never match.configured = base->live = dated. The reverse case (configured = dated,live = base) is not handled at all.The result: a model that is on the account's plan, present in
providers.<name>.models, and verifiably callable is silently removed from the authoritative live catalog, so it cannot be selected in the dashboard, inocx models live, or in the Codex model picker.GET /api/providersstill reportsdiscovery: { "status": "ok" }, so nothing on the API surface indicates a model was dropped.I expected the configured id to survive, because upstream does return this model — just under its base id (
deepseek-v4-pro) — which is exactly the situation the dated-alias fold exists to handle.Note this is not the same request as #1690. #1690 asks for a new opt-in
retainModelsfield to retain models that upstream genuinely does not return, and the maintainer review there correctly notes that retaining unconditionally would resurrect dead rows. This report is narrower: the existing fold already requires a matching live row to be present before it retains anything, so fixing it cannot resurrect a model the upstream does not advertise.Reproduction
Configure
alibaba-token-plan-intl(international Token Plan,ap-southeast-1) withliveModels: trueand amodelslist that matches the plan's text/reasoning models, includingdeepseek-v4-pro-0813.Confirm the configured list is intact:
Run live discovery:
Observe that
deepseek-v4-pro-0813is gone, whiledeepseek-v4-proremains.GET /api/modelsshows the same 10 rows.Confirm the dropped model is actually callable, both directly upstream and through the proxy (see logs below). Both return HTTP 200.
Version
2.36.0
Operating system
Windows 11 Pro 26200
Provider and model
alibaba-token-plan-intl / deepseek-v4-pro-0813
Logs or error output
Root cause
src/codex/catalog/provider-fetch.ts:938(identical onmainas of this report):Used by the fold in
mergeConfiguredModelsIntoLiveCatalog()atsrc/codex/catalog/provider-fetch.ts:1668-1686:For
configured = "deepseek-v4-pro-0813",live = "deepseek-v4-pro":liveId.startsWith("deepseek-v4-pro-0813-")isfalse-> no match (direction)."0813", and/^\d{8}$/rejects 4 digits (format).Both guards fail, so the id falls through to
droppedConfiguredIds. The only remaining retention path isshouldRetainConfiguredProviderModel()atsrc/codex/catalog/provider-fetch.ts:1631, which is hardcoded tokimi,xai, andopencode-free— so there is no escape hatch for this provider.alibaba-token-plan-intlis not inQUIET_AUTHORITATIVE_CATALOG_PROVIDERS, sowarnDroppedConfiguredIdsOnce()does emit a one-shot console warning, butGET /api/providersstill reportsdiscovery.status = "ok", which is what makes this look like "the model does not exist" from the dashboard.Suggested fix
Make the fold symmetric and accept
MMDD, while keeping it strict enough not to swallow non-date numeric suffixes (e.g. a-4096context-size suffix):This stays safe with respect to the concern raised in the #1690 review: the fold still only retains a configured id when a corresponding live row is actually present, and the retained row already goes through
applyProviderConfigHints(), so per-model context/reasoning hints from config still win.Happy to open a PR with unit tests covering both directions and both suffix widths if that is useful.
Redacted configuration
{ "providers": { "alibaba-token-plan-intl": { "adapter": "openai-chat", "baseUrl": "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1", "apiKey": "<redacted>", "defaultModel": "qwen3.8-max", "liveModels": true, "models": [ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash", "deepseek-v4-pro", "deepseek-v4-pro-0813", "glm-5.2", "deepseek-v4-flash-0731", "qwen3.8-flash" ] } } }The plan console lists
deepseek-v4-pro-0813,deepseek-v4-pro, anddeepseek-v4-flash-0731as three separate entitled Text Generation / Reasoning models, which is why all three are inmodels.Related: #1690 (feature request for an opt-in
retainModelsallow-list — complementary, not the same defect), #1671 (earlier user-facing symptom of a configured model being dropped by live discovery).