BigPizzaV3/CodexPlusPlus · error · anyhow::Error
model_providers.{provider_id} 必须是 TOML table
Error message
model_providers.{provider_id} 必须是 TOML table What it means
ensure_provider_table 保证 config.toml 中 model_providers.{provider_id} 存在且是 table,供后续写入 base_url / experimental_bearer_token 等字段。函数在键缺失或值不是 table 时会先插入新表,最后才用 as_table_mut 取可变引用——按构造逻辑该 bail 在当前实现中是防御性不变量,正常不可达。若真出现,说明 model_providers 段结构损坏或 provider_id 异常导致 toml_edit 键操作行为异常。
Source
Thrown at crates/codex-plus-core/src/relay_config.rs:2579
}
fn ensure_provider_table<'a>(
doc: &'a mut DocumentMut,
provider_id: &str,
) -> anyhow::Result<&'a mut Table> {
let providers = table_mut_or_insert(doc, "model_providers")?;
if !providers.contains_key(provider_id)
|| providers
.get(provider_id)
.and_then(Item::as_table)
.is_none()
{
providers.insert(provider_id, toml_edit::table());
}
providers
.get_mut(provider_id)
.and_then(Item::as_table_mut)
.ok_or_else(|| anyhow::anyhow!("model_providers.{provider_id} 必须是 TOML table"))
}
fn remove_provider_table(doc: &mut DocumentMut, provider_id: &str) {
if let Some(providers) = doc.get_mut("model_providers").and_then(Item::as_table_mut) {
providers.remove(provider_id);
if providers.is_empty() {
doc.as_table_mut().remove("model_providers");
}
}
}
fn retain_only_provider_table(doc: &mut DocumentMut, provider_id: &str) {
if let Some(providers) = doc.get_mut("model_providers").and_then(Item::as_table_mut) {
let provider = providers
.remove(provider_id)
.unwrap_or_else(toml_edit::table);
providers.clear();
providers.insert(provider_id, provider);View on GitHub (pinned to 1f431ae49b)
Solutions
- 打开 config.toml 检查 [model_providers] 段语法与结构,修复或删除损坏的 provider 条目
- 检查 provider_id 是否为常规标识符(避免点号/空串等异常值)
- 备份后让 CodexPlusPlus 重新应用 profile,重建 provider 表
Example fix
# before (config.toml, 损坏形态) model_providers.foo = 1 # after [model_providers.foo] name = "foo" base_url = "https://api.example.com/v1"
Defensive patterns
Strategy: validation
Validate before calling
anyhow::ensure!(!provider_id.trim().is_empty(), "provider_id 不能为空");
let doc: toml_edit::DocumentMut = config_contents.parse()?;
if let Some(item) = doc.get("model_providers") {
anyhow::ensure!(item.is_table() || item.as_table().is_some(), "model_providers 必须是 TOML table");
}
// 之后再调用会走 ensure_provider_table 的写入流程 Try / catch
if let Err(err) = apply_relay_profile_to_home_with_switch_rules_and_computer_use_guard(&home, &profile, &common, guard) {
if err.to_string().contains("必须是 TOML table") {
// 防御性不变量被打破:备份 config.toml,检查并重建 [model_providers] 段后重试
} else { return Err(err); }
} Prevention
- provider_id 使用常规标识符(字母数字连字符),避免点号/空白
- 避免外部进程并发写 config.toml
- 出现时先备份再让工具重建 provider 表
When it happens
Trigger: 写入/更新 provider(如 set_experimental_bearer_token_in_config、upsert_model_provider_config 内部)时,model_providers 表内部结构异常(例如被并发写入破坏、inline table / dotted-key 混用导致 as_table_mut 失败)。防御分支,正常构造路径下插入后必为 table。
Common situations: config.toml 被外部进程并发写坏;provider_id 含特殊字符(点号等)造成 toml_edit 键语义歧义;使用非常规工具编辑过 model_providers 段。
Related errors
- {table_name} 必须是 TOML 表
- {} must be a JSON object
- {key} must be a TOML table
- config.toml TOML parse failed: {error}
- 中转 Key 不能为空
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16).
Data as JSON: /api/errors/95d5b034e60e32eb.
Report an issue: GitHub.