Skip to content

Cold-start default model is stale: substring priority list picks gemini-3-pro-preview over every newer model #39125

Closed

Description

@molebox

What happened

I run opencode headless in fresh sandboxes every day (eval harness, 180 tasks/day) with a single gateway provider configured and no model set. Every single run picks google/gemini-3-pro-preview. That model is a generation behind and it never changes, so I dug into why. Verified on 1.18.7 (current npm latest).

The cause

packages/opencode/src/provider/provider.ts (dev branch):

const priority = ["gpt-5", "claude-sonnet-4", "big-pickle", "gemini-3-pro"]
export function sort<T extends { id: string }>(models: T[]) {
  return sortBy(
    models,
    [(model) => priority.findIndex((filter) => model.id.includes(filter)), "desc"],
    [(model) => (model.id.includes("latest") ? 0 : 1), "asc"],
    [(model) => model.id, "desc"],
  )
}

defaultModel falls through cfg.model -> recents in state/model.json -> sort(provider.models)[0]. With no config and no state, the priority list decides, and it has gone stale in a few compounding ways:

  1. Substring pins don't survive model generations. "gemini-3-pro" doesn't match google/gemini-3.1-pro-preview (that id contains gemini-3.1-pro), so the successor can never win. Meanwhile gpt-5.5 and claude-sonnet-4.6 do match their pins, but at lower priority indexes, so they lose to the older gemini pin.
  2. findIndex + "desc" means the last array entry is the highest priority. Easy to misread as first-wins. Either way the top preference is whatever was appended last, back when gemini-3-pro was current.
  3. The final tie-break is reverse-alphabetical id, which is the only reason -preview beats -image (p > i). Nothing checks capabilities at this point. If the catalog gained a -vision or -realtime sibling it would sort above -preview and become the default.
  4. Only status: deprecated/alpha get filtered before the sort, so a superseded-but-active preview model stays eligible forever.

Repro

Clean machine (no state/model.json), opencode.json with just a gateway provider and no model:

{
  "provider": {
    "vercel": { "options": { "apiKey": "{env:AI_GATEWAY_API_KEY}" } }
  }
}

opencode run "hello" picks google/gemini-3-pro-preview. Emulating sort() over that provider's catalog (499 models in the 1.18.7 build) ranks:

  1. google/gemini-3-pro-preview
  2. google/gemini-3-pro-image
  3. anthropic/claude-sonnet-4

Why nobody notices

The recents fallback shadows this for anyone who has ever picked a model interactively. Ephemeral environments hit the cold-start path every time - CI, sandboxes, containers, eval harnesses - and they're all quietly running on gemini-3-pro-preview today.

Suggested fix

Any of these kills the staleness class rather than today's instance:

  • Use release_date in the ranking. It's already on every model and getSmallModel already sorts by it - defaultModel could prefer the newest model within a preferred family/capability set.
  • Match on the catalog's family metadata (e.g. gemini-pro) instead of raw id substrings, so successor generations inherit the preference.
  • At minimum, filter for toolcall + text output ahead of the id tie-break so an image variant can't become the default by alphabetical accident.

Happy to PR whichever direction you prefer.

Environment: opencode 1.18.7 (npm opencode-ai, also confirmed in 1.17.0), single gateway provider via opencode.json, no model configured, clean state.

Activity

  1. molebox commented on Sep 19, 2026

    @molebox
    Author

    Is there any chance this will be fixed? Or shall we close this out? If not its not going to be fixed, we can just select the model i guess

  2. neriousy commented on Oct 19, 2026

    @neriousy
    Member

    This is fixed in v2: the stale model-name substring priority list is gone. The fallback now uses release-date ordering and selects a model that supports text, with a separate preference for certain free models when only free models are available. To get v2, run npm i -g @opencode/cli@latest. If you still see this on v2, please open a new issue with the details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions