Hmbown/CodeWhale · error
` ` in config.toml must be a table
Error message
`{segment}` in config.toml must be a table What it means
Thrown by table_like_at_path_mut when walking a dotted path inside config.toml: an intermediate segment resolves to a non-table value (e.g. a string or integer), but the operation needs a table to descend into or create. For Create lookups the library refuses to overwrite the existing scalar; for Existing lookups it reports the path as absent (Ok(None)) via the caller. It protects the TOML document from being silently reshaped.
Solutions
- Open config.toml and convert the offending key into a section: change `provider = "x"` to `[provider]` with the child keys under it
- Use a path whose intermediate segments are actual TOML tables
- Remove the scalar key if it is obsolete, then set the nested value
- Back up and regenerate the config section with the tooling's default writer
Example fix
// before (config.toml) model = "glm" model.temperature = 0.5 // after (config.toml) [model] name = "glm" temperature = 0.5
Defensive patterns
Strategy: validation
Validate before calling
// Check each intermediate segment is a TOML table before setting
fn segments_ok(doc: &toml::Value, path: &str) -> bool {
let segs: Vec<&str> = path.split('.').collect();
for s in &segs[..segs.len()-1] {
match doc.get(*s) {
Some(v) if v.is_table() => {}
_ => return false,
}
}
true
} Prevention
- Never convert a [section] header into a scalar key when hand-editing config.toml
- Validate the TOML document shape after merges or migrations
- Keep nested paths in sync with the document's actual structure
When it happens
Trigger: set_config_document_value with a path like "provider.name" where `provider` is currently a string/number/bool in config.toml; unset_config_document_value walking through the same conflict; editing config.toml by hand so a key that used to be a section [provider] becomes a scalar `provider = "x"`.
Common situations: Merging snippets into config.toml where a section header is accidentally converted to a key-value pair; older config versions with flat keys being addressed with nested paths.
Related errors
- Cannot export a non-string default_model without losing its…
- 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/b967a6f2ee766922.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/config_document.rs:449
let mut current: &mut dyn toml_edit::TableLike = root;
for segment in segments {
if current.get(segment).is_none() {
match lookup {
PathLookup::Create => {
let mut table = toml_edit::Table::new();
table.set_implicit(true);
current.insert(segment, toml_edit::Item::Table(table));
}
PathLookup::Existing => return Ok(None),
}
}
let item = current
.get_mut(segment)
.expect("segment exists or was inserted above");
match item.as_table_like_mut() {
Some(table) => current = table,
None => match lookup {
PathLookup::Create => bail!("`{segment}` in config.toml must be a table"),
PathLookup::Existing => return Ok(None),
},
}
}
Ok(Some(current))
}
#[cfg(test)]
mod tests {
#[test]
fn healing_keeps_a_non_table_extras_key_it_cannot_lift() {
// `extras` is where the config structs flatten unknown keys, so a
// scalar or array under that exact name round-trips through the typed
// path as ordinary user data. Healing used to `remove()` it before
// discovering it was not a table, dropping it on the very next
// `codewhale config set` — and reporting `healed == false` while doing
// so.
for body in [View on GitHub (pinned to 73e0f67d83)