{"record":{"id":"90711161bbcf026b","repo":"xai-org/grok-build","slug":"config-root-is-not-a-table-907111","errorCode":null,"errorMessage":"config root is not a table","messagePattern":"config root is not a table","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/util/config/mcp.rs","lineNumber":644,"sourceCode":"\npub const MANAGED_GATEWAY_DISABLED_CONNECTORS_KEY: &str = \"__managed_gateway_connectors\";\n\n/// Persist `disabled_tools` for a server under `[disabled_mcp_tools]` in config.toml.\n///\n/// Uses a dedicated top-level section (not `[mcp_servers]`) to avoid creating\n/// 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));","sourceCodeStart":626,"sourceCodeEnd":662,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/util/config/mcp.rs#L626-L662","documentation":"save_mcp_disabled_tools reads the config TOML into a TomlValue and calls as_table_mut expecting the document root to be a table; if parsing produced a non-table value (scalar/array) this error fires. In practice a valid TOML document is always a table, so this mostly triggers when parse_existing fallback or file content is malformed in an unusual way, or defensively when the value type is wrong.","triggerScenarios":"Calling save_mcp_disabled_tools when the loaded TOML root is not a TomlValue::Table — e.g. the file parses to a non-table value or a fallback path inserted a non-table root.","commonSituations":"Hand-edited config file whose top-level structure is invalid; a tool wrote a scalar/array TOML value; parser replaced a broken file with a non-table default.","solutions":["Open the config TOML and ensure the top level is a proper table (key = value sections), not a bare scalar or array.","Delete or repair the malformed config file and let the tool recreate it.","Validate the file with `toml` parsing (e.g. a quick script using the toml crate or a TOML linter) before running the save.","If the loader can produce non-table roots, replace the error with a normalization step that coerces the root to an empty table."],"exampleFix":"// before\nlet table = root.as_table_mut().ok_or_else(|| anyhow!(\"config root is not a table\"))?;\n// after — normalize instead of failing\nif !root.is_table() {\n    root = TomlValue::Table(TomlMap::new());\n}\nlet table = root.as_table_mut().unwrap();","handlingStrategy":"validation","validationCode":"fn config_root_is_table(path: &Path) -> bool {\n    std::fs::read_to_string(path).ok()\n        .and_then(|s| s.parse::<toml::Value>().ok())\n        .map(|v| v.is_table())\n        .unwrap_or(false)\n}","typeGuard":"fn as_table(v: &toml::Value) -> Option<&toml::value::Table> { v.as_table() }","tryCatchPattern":"match save_mcp_disabled_tools(&cfg, &server, &tools).await {\n    Err(e) if e.to_string().contains(\"config root is not a table\") => {\n        backup_and_reset_config(&cfg_path)?;\n        save_mcp_disabled_tools(&cfg, &server, &tools).await\n    }\n    other => other,\n}","preventionTips":[],"tags":["rust","toml","config","malformed-config"],"backgroundTag":"config-file-malformed","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}