fix(reply): clear 'model unavailable' reply + correct operator hint for retired runtime models#97611
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 28, 2026, 9:56 PM ET / 01:56 UTC. Summary PR surface: Source +10, Tests +23. Total +33 across 2 files. Reproducibility: yes. at source level: current main throws typed Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused classifier and hint change after maintainer review and green checks, with PR body or release context aligned to the actual typed-error scope. Do we have a high-confidence way to reproduce the issue? Yes at source level: current main throws typed Is this the best way to solve the issue? Yes, with one scope note: the typed AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 4a4657a1828f. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +10, Tests +23. Total +33 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
…-bound models When an agent's configured model is bound to an agent runtime (e.g. the "codex" runtime, whose catalog comes from the OpenAI ChatGPT-account app-server), and that model id is no longer offered by the runtime, model resolution fails with "Unknown model: <provider>/<model>". The appended hint told users to register the model in models.providers[].models[]. For runtime-owned models that advice is misleading: adding the registration makes resolution "succeed" only for the request to be rejected later by the provider — e.g. OpenAI returns 400 "model is not supported when using Codex with a ChatGPT account" once a model id such as gpt-5.3-codex is deprecated. Detect the agentRuntime binding on the configured agents.defaults.models entry and instead point the user at the runtime's live catalog (openclaw models list --provider <id>) and at switching the configured default to an available model. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… failure When a configured model is retired/renamed by the provider, model resolution fails (run.ts throws a typed FailoverError with reason "model_not_found") and the agent runner falls back to the generic "Something went wrong … use /new" reply. That copy is actively misleading for this failure: retrying or starting a new session can never help, because the model id itself must be changed in config. Users on a deprecated model (e.g. gpt-5.3-codex on a Codex ChatGPT account) just see the generic message on every message and on /new. Classify this failure into a dedicated user-facing reply that explains the model is unavailable and points at the config. Detection is structural — it keys off the typed FailoverError reason, not provider error text — so it stays robust as provider wording changes; free-text rejections without a typed reason remain the responsibility of the failover layer that owns error classification. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
614ac49 to
a436ebf
Compare
…or retired runtime models (openclaw#97611) * fix(models): don't advise models.providers[] registration for runtime-bound models When an agent's configured model is bound to an agent runtime (e.g. the "codex" runtime, whose catalog comes from the OpenAI ChatGPT-account app-server), and that model id is no longer offered by the runtime, model resolution fails with "Unknown model: <provider>/<model>". The appended hint told users to register the model in models.providers[].models[]. For runtime-owned models that advice is misleading: adding the registration makes resolution "succeed" only for the request to be rejected later by the provider — e.g. OpenAI returns 400 "model is not supported when using Codex with a ChatGPT account" once a model id such as gpt-5.3-codex is deprecated. Detect the agentRuntime binding on the configured agents.defaults.models entry and instead point the user at the runtime's live catalog (openclaw models list --provider <id>) and at switching the configured default to an available model. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(reply): show a clear "model unavailable" reply instead of generic failure When a configured model is retired/renamed by the provider, model resolution fails (run.ts throws a typed FailoverError with reason "model_not_found") and the agent runner falls back to the generic "Something went wrong … use /new" reply. That copy is actively misleading for this failure: retrying or starting a new session can never help, because the model id itself must be changed in config. Users on a deprecated model (e.g. gpt-5.3-codex on a Codex ChatGPT account) just see the generic message on every message and on /new. Classify this failure into a dedicated user-facing reply that explains the model is unavailable and points at the config. Detection is structural — it keys off the typed FailoverError reason, not provider error text — so it stays robust as provider wording changes; free-text rejections without a typed reason remain the responsibility of the failover layer that owns error classification. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
…or retired runtime models (openclaw#97611) * fix(models): don't advise models.providers[] registration for runtime-bound models When an agent's configured model is bound to an agent runtime (e.g. the "codex" runtime, whose catalog comes from the OpenAI ChatGPT-account app-server), and that model id is no longer offered by the runtime, model resolution fails with "Unknown model: <provider>/<model>". The appended hint told users to register the model in models.providers[].models[]. For runtime-owned models that advice is misleading: adding the registration makes resolution "succeed" only for the request to be rejected later by the provider — e.g. OpenAI returns 400 "model is not supported when using Codex with a ChatGPT account" once a model id such as gpt-5.3-codex is deprecated. Detect the agentRuntime binding on the configured agents.defaults.models entry and instead point the user at the runtime's live catalog (openclaw models list --provider <id>) and at switching the configured default to an available model. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(reply): show a clear "model unavailable" reply instead of generic failure When a configured model is retired/renamed by the provider, model resolution fails (run.ts throws a typed FailoverError with reason "model_not_found") and the agent runner falls back to the generic "Something went wrong … use /new" reply. That copy is actively misleading for this failure: retrying or starting a new session can never help, because the model id itself must be changed in config. Users on a deprecated model (e.g. gpt-5.3-codex on a Codex ChatGPT account) just see the generic message on every message and on /new. Classify this failure into a dedicated user-facing reply that explains the model is unavailable and points at the config. Detection is structural — it keys off the typed FailoverError reason, not provider error text — so it stays robust as provider wording changes; free-text rejections without a typed reason remain the responsibility of the failover layer that owns error classification. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
…or retired runtime models (openclaw#97611) * fix(models): don't advise models.providers[] registration for runtime-bound models When an agent's configured model is bound to an agent runtime (e.g. the "codex" runtime, whose catalog comes from the OpenAI ChatGPT-account app-server), and that model id is no longer offered by the runtime, model resolution fails with "Unknown model: <provider>/<model>". The appended hint told users to register the model in models.providers[].models[]. For runtime-owned models that advice is misleading: adding the registration makes resolution "succeed" only for the request to be rejected later by the provider — e.g. OpenAI returns 400 "model is not supported when using Codex with a ChatGPT account" once a model id such as gpt-5.3-codex is deprecated. Detect the agentRuntime binding on the configured agents.defaults.models entry and instead point the user at the runtime's live catalog (openclaw models list --provider <id>) and at switching the configured default to an available model. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(reply): show a clear "model unavailable" reply instead of generic failure When a configured model is retired/renamed by the provider, model resolution fails (run.ts throws a typed FailoverError with reason "model_not_found") and the agent runner falls back to the generic "Something went wrong … use /new" reply. That copy is actively misleading for this failure: retrying or starting a new session can never help, because the model id itself must be changed in config. Users on a deprecated model (e.g. gpt-5.3-codex on a Codex ChatGPT account) just see the generic message on every message and on /new. Classify this failure into a dedicated user-facing reply that explains the model is unavailable and points at the config. Detection is structural — it keys off the typed FailoverError reason, not provider error text — so it stays robust as provider wording changes; free-text rejections without a typed reason remain the responsibility of the failover layer that owns error classification. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
…or retired runtime models (openclaw#97611) * fix(models): don't advise models.providers[] registration for runtime-bound models When an agent's configured model is bound to an agent runtime (e.g. the "codex" runtime, whose catalog comes from the OpenAI ChatGPT-account app-server), and that model id is no longer offered by the runtime, model resolution fails with "Unknown model: <provider>/<model>". The appended hint told users to register the model in models.providers[].models[]. For runtime-owned models that advice is misleading: adding the registration makes resolution "succeed" only for the request to be rejected later by the provider — e.g. OpenAI returns 400 "model is not supported when using Codex with a ChatGPT account" once a model id such as gpt-5.3-codex is deprecated. Detect the agentRuntime binding on the configured agents.defaults.models entry and instead point the user at the runtime's live catalog (openclaw models list --provider <id>) and at switching the configured default to an available model. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * fix(reply): show a clear "model unavailable" reply instead of generic failure When a configured model is retired/renamed by the provider, model resolution fails (run.ts throws a typed FailoverError with reason "model_not_found") and the agent runner falls back to the generic "Something went wrong … use /new" reply. That copy is actively misleading for this failure: retrying or starting a new session can never help, because the model id itself must be changed in config. Users on a deprecated model (e.g. gpt-5.3-codex on a Codex ChatGPT account) just see the generic message on every message and on /new. Classify this failure into a dedicated user-facing reply that explains the model is unavailable and points at the config. Detection is structural — it keys off the typed FailoverError reason, not provider error text — so it stays robust as provider wording changes; free-text rejections without a typed reason remain the responsibility of the failover layer that owns error classification. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
OpenAI-backed runtimes reject plan/account-restricted models with HTTP
400 invalid_request_error ("The '<model>' model is not supported when
using Codex with a ChatGPT account."). Without a model_not_found match
the ambiguous-400 branch collapses this into a format failure, so users
get the generic retry//new copy for a config-only problem (openclaw#104490).
The 'when using' qualifier keeps capability rejections ("not supported
for tool calling") out of the class, preserving the openclaw#97611 contract.
Fixes openclaw#104490
OpenAI-backed runtimes reject plan/account-restricted models with HTTP
400 invalid_request_error ("The '<model>' model is not supported when
using Codex with a ChatGPT account."). Without a model_not_found match
the ambiguous-400 branch collapses this into a format failure, so users
get the generic retry//new copy for a config-only problem (openclaw#104490).
The 'when using' qualifier keeps capability rejections ("not supported
for tool calling") out of the class, preserving the openclaw#97611 contract.
Fixes openclaw#104490
#104878) * fix(agents): classify account-restricted model 400s as model_not_found OpenAI-backed runtimes reject plan/account-restricted models with HTTP 400 invalid_request_error ("The '<model>' model is not supported when using Codex with a ChatGPT account."). Without a model_not_found match the ambiguous-400 branch collapses this into a format failure, so users get the generic retry//new copy for a config-only problem (#104490). The 'when using' qualifier keeps capability rejections ("not supported for tool calling") out of the class, preserving the #97611 contract. Fixes #104490 * fix(agents): narrow account model rejection match --------- Co-authored-by: Altay <[email protected]>
openclaw#104878) * fix(agents): classify account-restricted model 400s as model_not_found OpenAI-backed runtimes reject plan/account-restricted models with HTTP 400 invalid_request_error ("The '<model>' model is not supported when using Codex with a ChatGPT account."). Without a model_not_found match the ambiguous-400 branch collapses this into a format failure, so users get the generic retry//new copy for a config-only problem (openclaw#104490). The 'when using' qualifier keeps capability rejections ("not supported for tool calling") out of the class, preserving the openclaw#97611 contract. Fixes openclaw#104490 * fix(agents): narrow account model rejection match --------- Co-authored-by: Altay <[email protected]>
openclaw#104878) * fix(agents): classify account-restricted model 400s as model_not_found OpenAI-backed runtimes reject plan/account-restricted models with HTTP 400 invalid_request_error ("The '<model>' model is not supported when using Codex with a ChatGPT account."). Without a model_not_found match the ambiguous-400 branch collapses this into a format failure, so users get the generic retry//new copy for a config-only problem (openclaw#104490). The 'when using' qualifier keeps capability rejections ("not supported for tool calling") out of the class, preserving the openclaw#97611 contract. Fixes openclaw#104490 * fix(agents): narrow account model rejection match --------- Co-authored-by: Altay <[email protected]>
What Problem This Solves
Fixes an issue where a chat user (e.g. on Telegram) gets "⚠️ Something went
wrong while processing your request. Please try again, or use /new to start a
fresh session" on every message — and
/newdoesn't help — when the agent'sconfigured model has been retired/renamed by the provider.
Concrete trigger: a config pinned to
openai/gpt-5.3-codexvia thecodexagent runtime (OpenAI ChatGPT account). After OpenAI stopped offering
gpt-5.3-codexto ChatGPT-account Codex, every turn failed. Two things made ithard to recover:
Chat user surface: model resolution throws a typed
FailoverErrorwithreason
model_not_found, but the reply classifier has no matching branch, sothe reply falls back to the generic "try again / use /new" copy — which is
actively wrong here, since retrying or starting a new session can never fix a
bad model id.
Operator surface: the underlying "Unknown model" error appends a hint
telling the operator to register the model in
models.providers[].models[].For a runtime-bound model that's misleading: runtime-owned models draw their
catalog from the runtime/account, not config, so following the hint can't
help (and can push the operator toward a config that the provider rejects
downstream).
Why This Change Was Made
Two small, layered changes, one per surface:
Chat reply (
provider-request-error-classifier.ts): add aprovider_model_unavailableclassification that returns a dedicated reply("the model is unavailable; this needs a config change; retrying/
/newwon'thelp") instead of the generic copy. Detection is structural and typed-only
— it keys off
isFailoverError(err) && err.reason === "model_not_found"(thereason
run.tssets at resolution time). It does not match provider errortext.
Operator hint (
embedded-agent-runner/model.ts): when the configuredagents.defaults.modelsentry is bound to anagentRuntime, stop advising amodels.providers[]registration and instead point at the runtime's livecatalog (
openclaw models list --provider <id>).Non-runtime/plain-provider behavior is unchanged.
Scope / non-goals
The chat classifier deliberately handles only the typed
model_not_foundfailover. It does not classify raw provider error strings or account-scoped
provider
400s (e.g. OpenAI's "model is not supported when using Codex with aChatGPT account"). Those carry no structured signal, and string-matching error
classification belongs to the failover layer that already owns it — not to this
downstream reply classifier. In practice the typed path covers the real
recovery case, and the operator-hint change steers operators away from the
config state that produces such provider
400s.User Impact
generic "try again" loop, for any failure that surfaces as a typed
model_not_found.(switch to an available model) instead of a
models.providers[]registrationthat can't help for runtime-bound models.
Evidence
provider-request-error-classifier.test.ts— a typedFailoverError(reason: "model_not_found")classifies toprovider_model_unavailable; a bare error string without the typed reason,and a different typed reason (
overloaded), both stay unclassified(locks in structural-only detection).
model.test.ts— a runtime-bound entry gets the runtime-catalog hint; aplain-provider entry keeps the existing registration hint.
gpt-5.5/gpt-5.4/gpt-5.4-minibut notgpt-5.3-codex; the failing paththrew exactly the typed
model_not_foundfailover handled here, and switchingthe default to
openai/gpt-5.5restored normal operation. (LiveChatGPT/Codex account path not re-run in CI.)