BigPizzaV3/CodexPlusPlus · error
必须是 TOML 表
Error message
{table_name} 必须是 TOML 表 What it means
After ensuring the target table key exists in the normalized common config, the function checks that the value at table_name is actually a TOML table. If an existing key of that name holds a scalar or array, inserting a sub-table under it is impossible, so the library bails naming the offending table.
Solutions
- Edit config.toml so the key (e.g. [mcp_servers]) is a proper table, removing the scalar/array value
- Back up and delete the conflicting scalar key, then retry the upsert
- Use delete_context_entry_from_common_config / normalization to clean the malformed section first
Example fix
// before (config.toml) mcp_servers = 3 // after (config.toml) [mcp_servers.my-server] command = "foo"
Defensive patterns
Strategy: validation
Validate before calling
let doc: toml_edit::DocumentMut = config.parse()?;
let table_name = context_table_name(kind)?;
if let Some(item) = doc.get(table_name) {
if item.as_table().is_none() {
return Err(anyhow::anyhow!("{table_name} 必须是 TOML 表"));
}
} Try / catch
match upsert_context_entry_in_common_config(kind, id, body, config) {
Err(e) if e.to_string().ends_with("必须是 TOML 表") => repair_or_rename_section(kind)?,
other => other?,
} Prevention
- Never assign scalar/array values to reserved section names in config.toml
- Run a pre-flight TOML schema check on the common config
- Back up config.toml before hand edits
When it happens
Trigger: Calling upsert_context_entry_in_common_config when the common config already contains e.g. model_context_window = 5 or context = ["a"] — a non-table value under the table name returned by context_table_name(kind).
Common situations: Hand-edited config.toml where a context section name was overwritten with a plain value; a merge tool flattened a table into a scalar; migrating an older config format into the new schema.
Related errors
- 必须是 TOML table
- model_providers. 必须是 TOML table
- config.toml TOML 解析失败:
- config.toml TOML 解析失败:
- Dream Skin theme backup belongs to a different config.toml
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/781fe91793979c58.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/relay_config.rs:1049
pub fn upsert_context_entry_in_common_config(
common_config: &str,
kind: &str,
id: &str,
toml_body: &str,
) -> anyhow::Result<String> {
let id = id.trim();
if id.is_empty() {
anyhow::bail!("上下文 id 不能为空");
}
let table_name = context_table_name(kind)?;
let body_doc = parse_toml_document(toml_body)?;
let normalized = normalize_duplicate_toml_text(common_config);
let mut doc = parse_toml_document(&normalized)?;
if !doc.as_table().contains_key(table_name) {
doc[table_name] = toml_edit::table();
}
if doc[table_name].as_table().is_none() {
anyhow::bail!("{table_name} 必须是 TOML 表");
}
doc[table_name][id] = Item::Table(body_doc.as_table().clone());
Ok(normalize_optional_toml(doc))
}
pub fn delete_context_entry_from_common_config(
common_config: &str,
kind: &str,
id: &str,
) -> anyhow::Result<String> {
let table_name = context_table_name(kind)?;
let normalized = normalize_duplicate_toml_text(common_config);
let mut doc = parse_toml_document(&normalized)?;
if let Some(table) = doc[table_name].as_table_mut() {
table.remove(id.trim());
if table.is_empty() {
doc.as_table_mut().remove(table_name);
}View on GitHub (pinned to b1ed92e5e4)