Hmbown/CodeWhale · error
Cannot export a non-string default_model without losing its…
Error message
Cannot export a non-string default_model without losing its value
What it means
When export scrubbing finds no canonical model identity (None), it must still remove default_model; but if the document's default_model is not a plain string, removing it would silently discard a value the tool cannot interpret, so it refuses via ensure!. This prevents non-string (e.g. table or array) default_model values from being dropped during export.
Solutions
- Convert default_model to a plain string model name before exporting
- Remove the malformed default_model manually after backing up its value
- Regenerate the config from defaults and reapply the intended model string
Example fix
# before
default_model = { name = "deepseek-chat" }
# after
default_model = "deepseek-chat" Defensive patterns
Strategy: validation
Validate before calling
let ok = doc.get("default_model").map_or(true, |v| v.as_str().is_some());
anyhow::ensure!(ok, "default_model must be a string before export"); Type guard
fn default_model_is_string(doc: &toml::Table) -> bool {
doc.get("default_model").map_or(true, toml::Value::is_str)
} Prevention
- Only ever assign strings to default_model
- Sanitize tooling that writes config values
- Validate TOML types after external edits
When it happens
Trigger: Exporting a document with no resolvable model identity while default_model exists in the document as a non-string TOML value (table, integer, array, etc.).
Common situations: Corrupted or hand-edited configs where default_model was written as a table; tooling that wrote unexpected types into the root key.
Related errors
- ` ` in config.toml must be a table
- active profile is missing or malformed
- Cannot export a root model alias into a non-table provider…
- Cannot export default_model into a non-table provider slot
- config entry must be a string
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/27cd3a6b508e9303.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/route_preferences.rs:250
// A root alias the DeepSeek route still consumes lands in this
// same slot on import; the live choice wins over the dead alias.
let root_covers_slot = active_slot == deepseek_slot
&& ["model", "default_text_model"]
.iter()
.any(|key| document.get(*key).and_then(toml::Value::as_str).is_some());
let folded = !value.is_empty()
&& !root_covers_slot
&& document_slot_value(document, &deepseek_slot).is_none();
if folded {
ensure!(
insert_document_slot_value(document, &deepseek_slot, &value),
"Cannot export default_model into a non-table provider slot"
);
}
document.remove("default_model");
}
None => {
ensure!(
!document.contains_key("default_model"),
"Cannot export a non-string default_model without losing its value"
);
}
}
Ok(())
}
fn project_root_key<'a>(path: &Path, key: &'a str) -> Option<&'a str> {
(codewhale_config::config_path_is_workspace_scoped(path)
&& matches!(key, "model" | "default_text_model"))
.then_some(key)
}
fn saved_config(store: &codewhale_config::ConfigStore) -> Result<Config> {
let rendered;
let body = if let Some(original) = store.original_body() {
originalView on GitHub (pinned to 73e0f67d83)