{"record":{"id":"502589bbe42628e1","repo":"xai-org/grok-build","slug":"disabled-mcp-tools-is-not-a-table","errorCode":null,"errorMessage":"disabled_mcp_tools is not a table","messagePattern":"disabled_mcp_tools is not a table","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/util/config/mcp.rs","lineNumber":650,"sourceCode":"/// incomplete server entries that fail to deserialize for managed servers.\npub(crate) async fn save_mcp_disabled_tools(\n    server_name: &str,\n    disabled_tools: &[String],\n) -> Result<()> {\n    let path = config_path();\n    let mut root: TomlValue = match tokio::fs::read_to_string(&path).await {\n        Ok(s) => toml::from_str(&s).unwrap_or(TomlValue::Table(TomlMap::new())),\n        Err(_) => TomlValue::Table(TomlMap::new()),\n    };\n    let table = root\n        .as_table_mut()\n        .ok_or_else(|| anyhow::anyhow!(\"config root is not a table\"))?;\n\n    let section = table\n        .entry(\"disabled_mcp_tools\")\n        .or_insert_with(|| TomlValue::Table(TomlMap::new()))\n        .as_table_mut()\n        .ok_or_else(|| anyhow::anyhow!(\"disabled_mcp_tools is not a table\"))?;\n\n    if disabled_tools.is_empty() {\n        section.remove(server_name);\n        if section.is_empty() {\n            table.remove(\"disabled_mcp_tools\");\n        }\n    } else {\n        let arr = disabled_tools\n            .iter()\n            .map(|s| TomlValue::String(s.clone()))\n            .collect();\n        section.insert(server_name.to_string(), TomlValue::Array(arr));\n    }\n\n    let toml_str = toml::to_string_pretty(&root)?;\n    let tmp = path.with_extension(\"toml.tmp\");\n    if let Some(parent) = path.parent() {\n        let _ = tokio::fs::create_dir_all(parent).await;","sourceCodeStart":632,"sourceCodeEnd":668,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/util/config/mcp.rs#L632-L668","documentation":"Within save_mcp_disabled_tools, the code fetches/creates the `disabled_mcp_tools` section and requires it to be a table. If the key already exists in the config but holds a non-table TOML value (string, integer, array), as_table_mut fails with this error.","triggerScenarios":"Calling save_mcp_disabled_tools when the config file contains `disabled_mcp_tools = <non-table value>` (e.g. `disabled_mcp_tools = true` or a list) instead of a `[disabled_mcp_tools]` section.","commonSituations":"Hand-edited or tool-mangled config where disabled_mcp_tools was written as a scalar/array; merge of configs from different schema versions.","solutions":["Edit the config file so `disabled_mcp_tools` is a proper table: `[disabled_mcp_tools]\\nserver = { }` or `disabled_mcp_tools = { server = [...] }` per the expected schema.","Remove the bogus `disabled_mcp_tools` line and rerun the save to regenerate it.","Validate the config with a TOML parser/schema check before saving.","Harden the code: if the existing value is not a table, replace it with a fresh table instead of erroring."],"exampleFix":"// before\n# config.toml\ndisabled_mcp_tools = true\n// after\n# config.toml\n[disabled_mcp_tools]\n[disabled_mcp_tools.my_server]\nenabled = false","handlingStrategy":"validation","validationCode":"fn disabled_tools_section_ok(path: &Path) -> bool {\n    std::fs::read_to_string(path).ok()\n        .and_then(|s| s.parse::<toml::Value>().ok())\n        .and_then(|v| v.get(\"disabled_mcp_tools\").cloned())\n        .map(|v| v.is_table())\n        .unwrap_or(true) // absent key is fine\n}","typeGuard":"fn is_section_table(v: Option<&toml::Value>) -> bool {\n    v.map(|v| v.is_table()).unwrap_or(true)\n}","tryCatchPattern":"match save_mcp_disabled_tools(&cfg, &server, &tools).await {\n    Err(e) if e.to_string().contains(\"disabled_mcp_tools is not a table\") => {\n        repair_section_to_table(&cfg_path, \"disabled_mcp_tools\")?;\n        save_mcp_disabled_tools(&cfg, &server, &tools).await\n    }\n    other => other,\n}","preventionTips":["Never hand-write disabled_mcp_tools as a scalar or array","Coerce non-table sections to fresh tables instead of erroring","Run a TOML schema check after manual config edits","Keep one writer for the config to avoid schema drift"],"tags":["rust","toml","config","schema-mismatch"],"backgroundTag":"config-file-malformed","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}