Repository navigation
[Bug] A retired provider model id fails every turn with the raw upstream body, and nothing re-validates a configured id #11838
huotianjun
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
{"title": "[Bug] A retired provider model id fails every turn with the raw upstream body, and nothing re-validates a configured id", "body": "# [Bug] A retired provider model id fails every turn with the raw upstream body, and nothing re-checks a configured id\n\nPackage:
dsh-llm-pi-ai(stream.tserror classification) +@deepseek-ai/dsh-client-ui-chat(failure rendering) + model-config validation\nObserved:dsh 0.2.2-alpha.2,pi-ai 1.3.0, routeopencode-go(openai-completions,https://opencode.ai/inference/go/openai/v1)\n\n## Summary\n\nOpenCode Go retired the idsdeepseek-flashanddeepseek-v4-flash. A profile that still lists\nthem keeps showing them in the model picker, and every turn sent to one of those ids fails with:\n\n\nThis turn failed400: {\"type\":\"server_error\",\"message\":\"Upstream request failed: Model is unavailable.\"}\n\n\nTwo separate gaps make this worse than a wrong pick:\n\n1. The provider's own reason is passed through as an unparsed JSON body, classified as a\n genericINVALID_REQUEST, with no hint that the fix is "pick another model id".\n2. Nothing re-validates a configured model id. The retired id also sits in\nsummarizationModeland inagent-default-model, so context compaction and the default\n route fail the same way \u2014 not just the turn the user started.\n\n## Reproduction\n\nRoute works and fails side by side on the same credential, same gateway, same header:\n\n\ndeepseek-v4.1-flash -> 200\ndeepseek-v4-pro -> 200\ndeepseek-flash -> 400 {\"error\":{\"type\":\"server_error\",\"message\":\"Upstream request failed: Model is unavailable.\"}}\ndeepseek-v4-flash -> 400 {\"error\":{\"type\":\"server_error\",\"message\":\"Upstream request failed: Model is unavailable.\"}}\n\n\npi-ai 1.3.0's bundled catalog agrees \u2014dist/providers/data/opencode-go.jsonhas 29 entries and\nonly two DeepSeek ones:deepseek-v4-pro,deepseek-v4.1-flash. Neitherdeepseek-flashnor\ndeepseek-v4-flashis there.\n\nBut a profilemodels:block can still declare them (the config accepts ids the catalog does not\nknow), and they keep rendering in the picker:\n\nyaml\n# profiles/web/cordis.patch.yml\n models:\n - id: deepseek-v4-flash # gone from the provider\n - id: deepseek-flash # gone from the provider\n summarizationModel: deepseek-flash # same dead id inside compaction\n\n# profiles/deepvoice/cordis.patch.yml\n model: deepseek-flash # agent-default-model\n summarizationModel: deepseek-flash\n\n\n## Current behavior\n\n- The turn error is the raw upstream body with the HTTP status prefixed:400: {json}.\n-classifyPiAiError(packages/llm/llm-pi-ai/src/stream.ts) has branches forAUTH,\nQUOTA_EXCEEDED,RATE_LIMIT,INVALID_REQUEST(413/400),SERVER(5xx),TIMEOUT,\nTRANSPORT, and aPI_AI_ERRORfallback \u2014 there is no model-unavailable branch, so this\n lands onINVALID_REQUESTby its\\b400\\bmatch.\n-failureMessage(packages/client/ui-chat/src/client/chat/MessageItem.tsx:51) maps only\nACCOUNT_SIGNED_OUT,INTERRUPTED,ACCOUNT_SIGN_IN_REQUIRED,QUOTA/ACCOUNT_QUOTA,AUTH,\n and otherwise returns the raw message. So the user sees JSON, and the localized\nmessage.turnErrortitle is prefixed directly to it.\n- The id stays in the picker indefinitely, and nothing marks it dead after the first 400.\n\n## Expected behavior\n\nAny one of these would have turned a dead end into a one-line fix:\n\n- A distinctMODEL_UNAVAILABLEclassification for provider responses that say the model is\n gone, with a message naming the id and the route and suggesting that the route's other models\n are still valid \u2014 rather than echoing the gateway's JSON.\n- A configured model id that the provider rejects as unavailable gets flagged in the picker and in\nsummarizationModel/agent-default-modelvalidation, instead of silently failing every turn.\n- Optionally: compaction falling back to the conversation's current target when its configured\n summarization model is unavailable, so a dead summarization id does not block compaction.\n\n## Environment\n\n-dsh 0.2.2-alpha.2,pi-ai 1.3.0, Linux, routeopencode-govia profilewebanddeepvoice.\n- Reproduced 2026-11-02.\n\n## Relationship to existing reports\n\nThis is the opposite direction from #8227 ("Provider model lists go stale" \u2014 live models missing\nfrom the bundled catalog). There the symptom is silence: a live model cannot be picked. Here the\nsymptom is a hard 400 on ids that are already configured, including internal ones\n(summarizationModel), and the upstream reason reaches the user unparsed. A fix for either\ndirection wants the same thing #8227 already argues for: treat the catalog as refreshable, and\nstop trusting a pinned static list as the authority on what a provider serves.\n"}All reactions