Repository navigation
Description
Summary
Custom, config-declared models (a provider entry whose models live only in config, with no models.dev catalog entry) are toggled hidden by default in every fresh client of the web/desktop app: the server reports time.released: 0 ("unknown"), the client normalizes that into the valid date "1970-01-01", and the default-visibility rule (visible() in the models context) then treats the model as 56 years old and hides it. Because visibility preferences are persisted only in client-local storage (browser localStorage / desktop storage), nothing on the server remembers the user's "show" toggle — every new browser profile, cleared storage, or another machine starts with these models hidden again.
Environment
- opencode version: 2.0.6 (
opencode serve --service, channellatest; same code path verified on 2.0.4 from the served bundle) - OS: Linux x86_64 (server host)
- Terminal: N/A — defect is in the web/desktop app served by
opencode serve - Shell: /bin/bash (server host)
- Install/channel: npm, latest
- Active plugins: none needed; reproduces with a single custom-provider config entry
Reproduction
- Add a custom provider whose models exist only in configuration (no
models.devmatch):{ "$schema": "https://opencode.ai/config.json", "providers": { "acme-ai": { "name": "Acme AI", "package": "@opencode/ai/providers/openai-compatible", "settings": { "baseURL": "https://api.example.com/v1" }, "models": { "acme-model-1": { "name": "Acme Model 1" } } } } } - (Re)start the server. In
GET /api/model,acme-ai/acme-model-1is returned with"enabled": truebut"time": { "released": 0 }. - Open the web app in a fresh browser profile (or incognito window).
- Settings → Models: "Acme Model 1" is toggled OFF ("hidden") despite there being no stored preference. The model picker does not list it either.
- Toggle it ON — it becomes usable in that browser.
- Clear site data (or open another browser/machine). The model is hidden again.
Expected Behavior
Models whose release date is unknown should be visible by default — the client fallback even encodes that intent (!date.isValid → visible). Alternatively, configuration should let me set a model's release date so catalog-less models can be dated.
Actual Behavior
Every config-declared model is hidden by default in every fresh client. The only toggle lives in per-browser/per-app storage, so the setting appears to "reset" whenever client state is fresh or lost.
Root cause (verified on served v2.0.6 bundle + live /api/model output)
- Server: models without a catalog entry are emitted with
"time": { "released": 0 }(epoch 0 = unknown). - Client normalization coerces that epoch-0 into a real date string (in the served runtime chunk):
release_date: new Date(e.time.released).toISOString().slice(0, 10) // -> "1970-01-01"
- Default visibility rule (models context), minimally de-minified:
where
visible(model) = stored !== "hide" && (stored === "show" || latestSet.has(key) || !releaseDate.isValid)
latestSet= models whose parsed release date is within the last 6 months."1970-01-01"parses as a valid Luxon date, so the!isValidescape never fires, and the 6-month window fails → hidden. - Storage:
visibleoverrides are kept viaPersist.global("model", ...)targeting client-local storage only; none of it is posted back to the server. Selecting a model also writessetVisibility(key, true), which is why "my main models stay visible in the browser I usually use" while other clients reset. - Config cannot override: the native V2 config model schema offers exactly
family,name,api,capabilities,request,variants,cost,disabled,limit— no date field. The V1 schema'smodels.<id>.release_dateexists but is dropped silently bymigrateModel()in the V1→V2 compatibility layer.
Suggested fix
Either of:
- Treat
released === 0as unknown when building the client catalog entry (leaverelease_dateunset/null), so the existing!isValidfallback defaults them to visible; and/or - expose a per-model release-date field in config (or preserve V1
release_datethrough migration).
Separately, persisting visibility preferences per-server (not per-browser) would fix the "loses toggles" half of the experience — tracked by #21310 (closed as not planned).
Additional Context
- Found while investigating why custom-provider models "disappeared from the picker after every server restart" — the restarts merely coincided with fresh/cleared clients; the defaults do the hiding.
- Only catalog-declared models are immune: they carry real release dates and (when <6 months old) pass the
latestSetwindow. - Possibly related older reports: /model TUI picker does not display all custom provider models (models load correctly but don't appear in UI) #6169 (TUI picker missing custom models), Unable to choose from all of the custom models in the web UI #6798 (web UI cannot select configured custom models). This one is a narrower, mechanistically identifiable defect.
- Related feature requests: feat: support disabling individual models within a provider #21035, Feature: Support disabled_models config to hide specific models #9203 (per-model hiding support).
Activity
github-actions commentedon Sep 17, 2026
This issue might be a duplicate of existing issues. Please check:
familyfield — shares the same root cause (visible()/latestSetlogic inpackages/app/src/context/models.tsx) and the same symptom of models being hidden in the picker. The fix described there (groupByfallback or treating family-less models like date-less ones) may be closely related to or subsume this issue.sergiopadure commentedon Sep 17, 2026
Note on the duplicate-detector link to #47557: related, but not a duplicate.
Both defects funnel through the same
visible()/latestSetlogic in the models context, but they feed it differently:family; remedagroupBy(family)drops them fromlatest(), so they never enterlatestSet.time.releasedis0; the epoch value is coerced into the valid string"1970-01-01", which fails the< 6 monthscheck beforegroupByis reached. Family grouping never participates.Two consequences:
family-override workaround documented in Web/Desktop model picker permanently hides catalog models that lack a family field (e.g. opencode-go/omen-alpha) #47557 does not help here — config-declared models are filtered out by the date coercion regardless of their family.Suggesting one shared repair zone, though: making the
visible()fallback handle "no meaningful date" (treatreleased === 0/ missing as unknowable → visible) addresses this issue, and keeping undefined-family models in the latest grouping addresses #47557. A config-level release date for custom models would fix this issue's class independently of either.fix: treat released=0 as unknown date to keep config models visible
hejackson2 commentedon Sep 27, 2026
Confirmed on OpenCode Desktop 2.0.18 on macOS (Darwin arm64).
I configured a custom OpenAI-compatible provider in
~/.config/opencode/opencode.jsonc:mac-mini-01@opencode/ai/providers/openai-compatiblehttp://localhost:11450/v1antares-1b-q8gemma3-12bqwen2.5-coder-7bqwen3-8bThe Desktop
/modelpicker does not show the provider or any of thesemodels. The CLI does show them.
The shared service is healthy and returns all four in
/api/modelwithenabled: true; two (antares-1b-q8andqwen3-8b) also reportcapabilities.tools: true. Both the Desktop app and service are version2.0.18. Restarting Desktop and running
opencode service restartdid notchange the result.
Environment:
/opt/homebrew/bin/bashopencodejr commentedon Oct 28, 2026
Thanks for the detailed analysis. I could reproduce this on 2.0.6, and it still happens on the latest
dev(950111ff2).What I did:
acme-aiprovider withacme-model-1(no models.dev entry), then ranopencode serve.GET /api/modelreturns the model as active withtime: {"released": 0}./api/providerand/api/modelresponses through the app's ownnormalizeProviderList. The model'srelease_datecomes out as"1970-01-01".models.tsxthen returnsfalse. The "no parseable release date stays visible" fallback never applies, and the model is outside the ~6-month "latest" window, so it's hidden by default.The script below reproduces this. I couldn't open the web UI itself, since there's no browser in my sandbox, so I copied the visibility rule into the script.
Possible workaround on current
dev(I haven't tested it, and it isn't in 2.0.6): model config there acceptsreleaseDate(e.g."releaseDate": "2026-10-01"), so a config-only model can be given a date.Related:
/modelsdialog, likely the same causeReproduction script (repro.sh)
Run it:
bash repro.shsergiopadure commentedon Oct 28, 2026
Confirmed the same chain on the current release channel. One correction worth adding for anyone landing here on a recent build: the
releaseDateknob is not dev-only — it ships in v2.2.0.In the shipped v2.2.0 build:
Config.ModelacceptsreleaseDate, validated asYYYY-MM-DD(orYYYY-MM), optional;if (c.releaseDate !== undefined) g.time = { released: Date.parse(c.releaseDate) }, i.e. the config value flows straight intotime.released, replacing the forced epoch 0.So on v2.2.0+, a config-declared model can be made default-visible by dating it in config:
That's a workable per-model workaround for this issue's class. A default-visible change for undated models (treat
released === 0as unknown, per the issue body's suggested fix, or preserving V1release_datethrough migration) would still be the cleaner resolution.sergiopadure commentedon Oct 28, 2026
A correction to my comment above, and a request.
Correction. On v2.2.0,
releaseDatealone does not make every config-declared model visible by default. Thepicker's default rule (
packages/app/src/providers/models/models.tsx) has two more conditions:latestModels()(lines 22-34) keeps only the newest model per provider andfamily. Config-declared models have nofamilyunless one is set, so all of a provider's dated models share one group and only one of them becomes visible(on equal dates, the first listed). This is the grouping problem from Web/Desktop model picker permanently hides catalog models that lack a family field (e.g. opencode-go/omen-alpha) #47557.
Math.abs(Date.now() - released) < sixMonths(line 80), so a dated model is hidden again six monthsafter its date.
Making a whole custom provider visible therefore needs a distinct made-up
familyper model and a release date thatsomeone keeps moving forward. The V1
provider.<id>.models.<id>.release_datefield is still dropped (it is listed inunsupportedModelinpackages/core/src/config/normalize.ts), so V1 configs have to move toprovidersfirst. For alarge configuration, that is not a practical workaround.
Request. Could there be a supported way to make all models visible by default, without per-browser toggles? Any of
these would do:
time.released === 0as unknown when building the client entry (norelease_date), so config-declaredmodels fall under the existing "no parseable release date stays visible" fallback (
models.tsxlines 127-128).This is the fix suggested in the issue body.
"visible": true), read bythe picker before the date and family rules.
Model selections in the Model Choice tab reset after restarting the web app #21310).
joe-ieta commentedon Oct 30, 2026
Confirmed on Windows V2 desktop and the current dev catalog adapter. The service loads the global JSONC provider/model successfully and emits released: 0; the app adapter turns it into 1970-01-01 and the default filter hides it.
I have a focused two-file fix at joe-ieta/opencode@anomalyco:opencode:dev...joe-ieta:opencode:model-release-date . It preserves the zero sentinel as an empty date, using the existing unknown-date visibility fallback. The updated upstream adapter tests fail before the fix (14 pass / 1 fail) and pass afterward (15 tests, repeated 10 times). Production UI checks also confirm default visibility, explicit hide/show and unchanged filtering for old dated models.
The Windows follow-up report is #60479; this issue remains the original root-cause report. The focused fix is now submitted as #60496. Please classify the follow-up report as a bug if useful; my account cannot add repository labels.