Repository navigation
Description
Description
A session's model lives in session_v2.model, written only at creation or by an explicit switchModel. When that model later leaves the catalog, the session hard-fails on every turn with Model unavailable: <provider>/<model> and cannot recover: editing config, reloading, and restarting all reproduce it.
I'm not asking why the value goes stale — #53060, #49765 and #52333 cover that. I'm reporting that there is no recovery once it is stale.
packages/core/src/session/runner/model.ts validates the stored ref against the live catalog and then fails:
const selected = (yield* available()).find(
(model) => model.providerID === session.model?.providerID && model.id === session.model.id,
)
if (!selected)
return yield* new ModelUnavailableError({ ... })It doesn't fall back to the agent's configured model and doesn't write a correction back. ModelUnavailableError maps to provider.no-route, so the turn is dead.
The column has exactly two writers — session/projector.ts:461 (session.created) and :571 (session.model.selected). No per-turn refresh, and nothing subscribes to provider.updated/model.updated, so a stale ref is never invalidated. SessionStore.get is an uncached row read, so a restart just re-reads the same row.
One amplifier: on submit the composer re-sends the stored model (packages/app/src/composer/submit.ts), and the selection memo prefers it over catalog availability — there's an explicit comment, "Catalog availability must not replace a user's choice with a default on the next submission" (packages/app/src/providers/models/selection.tsx). switchModel dedupes identical selections (session.ts:101), so the dead ref is re-asserted every turn instead of cleared. There's also no CLI or API to repair a stored session model.
Possible fixes: re-resolve or clear session_v2.model on a catalog miss, fall back to the agent model instead of failing closed, and/or invalidate stored models when the catalog updates.
Plugins
oh-my-opencode-slim 3.0.6 (found via that repo's issue #1689)
OpenCode version
2.0.30 (original reporter on 2.1.4)
Steps to reproduce
- Pin a session to a model currently in the catalog and send a message, so it's persisted to
session_v2.model. - Remove that model from the catalog (delete the provider entry, disable the model, or drop the plugin that contributed it).
- Send another message in the same session →
Model unavailable: <provider>/<model>. - Edit config, reload, restart → same failure. Only a new session or a manual model switch recovers.
Screenshot and/or share link
N/A — visible in the server log and the session row.
Operating System
macOS 26.0.1 (original report: Linux)
Terminal
N/A
Traced by reading the source rather than reproducing locally, so the mechanism is verified but the repro above is reconstructed — happy to run it against a scratch instance if useful.
Activity
opencode-agent commentedon Oct 26, 2026
Thanks for the detailed trace! To restate the request: when a session's stored model drops out of the catalog, OpenCode should give you a way back instead of failing every turn with
Model unavailable. That could mean re-resolving or clearing the stored model, falling back to the agent's model, invalidating stored models when the catalog changes, or a CLI/API to repair a session's model.I'm tagging this as a feature request rather than a bug, because failing closed looks deliberate.
SessionRunnerModel.resolveonly falls back to the agent's model or a default when no model is stored. The app also keeps an explicit choice on purpose ("Catalog availability must not replace a user's choice with a default on the next submission"). Not silently rerouting to another model matters for cases like #52333. As you noted, switching the model manually does recover the session. Still, a clear repair path, or a visible fallback, makes sense as an improvement, and maintainers will consider it.Related issues:
Model unavailableerror, different cause (env-authenticated providers)holny commentedon Oct 26, 2026
Confirmed against the current v2 branch —
SessionRunnerModel.resolvedoes a straight catalogfindfor the stored ref and throwsModelUnavailableErroron a miss, with no fallback to the default resolution and nothing writing back a correction. The only two writers ofsession_v2.modelare the projector's created/selected events, and the composer re-asserts the stored ref on every submit, so a stale value never heals.Happy to take this. Leaning toward the smallest fix: on a catalog miss, fall back to the default model resolution instead of failing the turn, plus a regression test for a model that leaves the catalog mid-session. Not sure yet whether the stale ref should also be cleared once we fall back — could go either way.