Hmbown/CodeWhale · error
Choose a model for the destination provider before…
Error message
Choose a model for the destination provider before switching from a legacy custom connection; the saved configuration was not changed
What it means
`reconcile_root_model_aliases` (called from `persist_provider_selection`) refuses to reconcile when the outgoing saved connection is a legacy literal `Custom` provider (no persisted id) that differs from the incoming selection. Switching away from such a connection requires a concrete destination model; without one the save is aborted unchanged to avoid destroying the legacy alias state.
Solutions
- Select a model for the destination provider before saving the provider switch
- If the legacy custom connection is no longer wanted, explicitly remove it first, then set the new provider+model together
Defensive patterns
Strategy: validation
Validate before calling
if outgoing.provider == ApiProvider::Custom
&& outgoing.persisted_id().is_none()
&& outgoing != incoming
&& destination_model.is_none() {
eprintln!("select a destination model before switching from legacy custom");
} Type guard
fn is_legacy_custom(c: &Connection) -> bool {
c.provider == ApiProvider::Custom && c.persisted_id().is_none()
} Try / catch
match persist_result {
Err(e) if e.to_string().contains("legacy custom connection") => prompt_for_destination_model_then_retry(),
other => other?,
} Prevention
- Always resolve a concrete model for the destination before a provider switch
- Migrate legacy free-form custom connections to persisted ids
- Surface model selection in the UI whenever the current connection is legacy custom
When it happens
Trigger: Calling `persist_provider_selection` while the current connection is a legacy custom connection (`provider == Custom` with no `persisted_id`) and the caller did not supply a model for the destination provider.
Common situations: TUI provider-switch flow where the user picks a new provider but no default model is selected; configs migrated from the old free-form custom-provider format; automated config edits changing the provider without specifying a model.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Antigravity is a retired, non-runnable legacy provider…
- Antigravity is a retired, non-runnable legacy provider…
- Antigravity is a retired, non-runnable legacy provider…
- Cannot resume agent on its saved provider
- Config migration skipped
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/135292eac989f841.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/config_persistence.rs:311
}
// An empty or control-bearing alias names no saved model. Nothing to
// relocate, and clearing it loses nothing.
if value.trim().is_empty() || value.chars().any(char::is_control) {
unset_document_value(doc, &[ROOT_KEY])?;
return Ok(());
}
// No leaf can hold this value. Keep the only copy of the user's choice
// rather than discard it for a document the incoming route loads as soon as
// it saves a model of its own.
let outgoing = previous
.active_provider_identity(previous.api_provider())
.ok();
if outgoing.as_ref().is_some_and(|outgoing| {
outgoing != incoming
&& outgoing.provider == ApiProvider::Custom
&& outgoing.persisted_id().is_none()
}) {
anyhow::bail!(
"Choose a model for the destination provider before switching from a legacy custom connection; the saved configuration was not changed"
);
}
let Some(outgoing) = outgoing.as_ref().filter(|outgoing| *outgoing != incoming) else {
return Ok(());
};
let mut scoped = switched;
scoped.scope_to_provider_identity(outgoing);
if scoped
.provider_config_for(outgoing.provider)
.and_then(|entry| entry.model.as_deref())
.is_none()
{
set_provider_model_document(
doc,
outgoing.provider,
outgoing.persisted_id().unwrap_or(&outgoing.key),
&value,View on GitHub (pinned to 73e0f67d83)