{"record":{"id":"27cd3a6b508e9303","repo":"Hmbown/CodeWhale","slug":"cannot-export-a-non-string-default-model-without-losing-its","errorCode":null,"errorMessage":"Cannot export a non-string default_model without losing its value","messagePattern":"Cannot export a non-string default_model without losing its value","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/route_preferences.rs","lineNumber":250,"sourceCode":"            // A root alias the DeepSeek route still consumes lands in this\n            // same slot on import; the live choice wins over the dead alias.\n            let root_covers_slot = active_slot == deepseek_slot\n                && [\"model\", \"default_text_model\"]\n                    .iter()\n                    .any(|key| document.get(*key).and_then(toml::Value::as_str).is_some());\n            let folded = !value.is_empty()\n                && !root_covers_slot\n                && document_slot_value(document, &deepseek_slot).is_none();\n            if folded {\n                ensure!(\n                    insert_document_slot_value(document, &deepseek_slot, &value),\n                    \"Cannot export default_model into a non-table provider slot\"\n                );\n            }\n            document.remove(\"default_model\");\n        }\n        None => {\n            ensure!(\n                !document.contains_key(\"default_model\"),\n                \"Cannot export a non-string default_model without losing its value\"\n            );\n        }\n    }\n    Ok(())\n}\n\nfn project_root_key<'a>(path: &Path, key: &'a str) -> Option<&'a str> {\n    (codewhale_config::config_path_is_workspace_scoped(path)\n        && matches!(key, \"model\" | \"default_text_model\"))\n    .then_some(key)\n}\n\nfn saved_config(store: &codewhale_config::ConfigStore) -> Result<Config> {\n    let rendered;\n    let body = if let Some(original) = store.original_body() {\n        original","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/route_preferences.rs#L232-L268","documentation":"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.","triggerScenarios":"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.).","commonSituations":"Corrupted or hand-edited configs where default_model was written as a table; tooling that wrote unexpected types into the root key.","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"],"exampleFix":"# before\ndefault_model = { name = \"deepseek-chat\" }\n# after\ndefault_model = \"deepseek-chat\"","handlingStrategy":"validation","validationCode":"let ok = doc.get(\"default_model\").map_or(true, |v| v.as_str().is_some());\nanyhow::ensure!(ok, \"default_model must be a string before export\");","typeGuard":"fn default_model_is_string(doc: &toml::Table) -> bool {\n    doc.get(\"default_model\").map_or(true, toml::Value::is_str)\n}","tryCatchPattern":null,"preventionTips":["Only ever assign strings to default_model","Sanitize tooling that writes config values","Validate TOML types after external edits"],"tags":["config","toml","type-mismatch"],"backgroundTag":"config-type-mismatch","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}