{"record":{"id":"054b5e4e298c86ef","repo":"xai-org/grok-build","slug":"mcp-servers-is-not-a-table","errorCode":null,"errorMessage":"[mcp_servers] is not a table","messagePattern":"\\[mcp_servers\\] is not a table","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/claude_import.rs","lineNumber":980,"sourceCode":"        if !env_table.contains_key(*key) {\n            env_table.insert(key.to_string(), TomlValue::String(value.to_string()));\n            count += 1;\n        }\n    }\n    count\n}\n\n/// Merge MCP server configs into `[mcp_servers]`. Existing servers are NOT overwritten.\nfn merge_mcp_servers(\n    table: &mut TomlMap<String, TomlValue>,\n    servers: &[(&str, &McpServerConfig)],\n) -> anyhow::Result<usize> {\n    let mcp = table\n        .entry(\"mcp_servers\")\n        .or_insert_with(|| TomlValue::Table(TomlMap::new()));\n    let mcp_table = mcp\n        .as_table_mut()\n        .ok_or_else(|| anyhow::anyhow!(\"[mcp_servers] is not a table\"))?;\n\n    let mut count = 0;\n    for (name, config) in servers {\n        // Don't overwrite existing server entries.\n        if !mcp_table.contains_key(*name) {\n            let serialized = toml::Value::try_from(*config)\n                .map_err(|e| anyhow::anyhow!(\"failed to serialize MCP server {name}: {e}\"))?;\n            mcp_table.insert(name.to_string(), serialized);\n            count += 1;\n        }\n    }\n    Ok(count)\n}\n\n/// Merge a list of path strings into `[paths] <key>` (an array of strings).\n///\n/// Existing entries are preserved; new entries that aren't already present\n/// are appended. Returns the number of newly added entries.","sourceCodeStart":962,"sourceCodeEnd":998,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/claude_import.rs#L962-L998","documentation":"merge_mcp_servers found an mcp_servers key whose value is not a table, so per-server config tables cannot be inserted. A missing key gets an empty table; a present non-table value triggers this error instead of overwriting the user's data.","triggerScenarios":"apply_items_to_config processes MCP servers while the config has e.g. mcp_servers = true or a string/array value under mcp_servers.","commonSituations":"User set mcp_servers = \"disabled\" by mistake; another tool wrote the key as a non-table; hand-edit replaced the section with a scalar.","solutions":["Restore mcp_servers to a table: [mcp_servers] or mcp_servers = {} in the config.","Delete the malformed key and re-run the import so the table is created.","Move existing server definitions under [mcp_servers.<name>] sections."],"exampleFix":"// before (config.toml)\nmcp_servers = \"disabled\"\n\n// after\n[mcp_servers]\n# server entries go here","handlingStrategy":"validation","validationCode":"let root: toml::Value = toml::from_str(&std::fs::read_to_string(&path)?)?;\nif let Some(v) = root.get(\"mcp_servers\") {\n    if !v.is_table() {\n        anyhow::bail!(\"mcp_servers must be a [mcp_servers] table\");\n    }\n}","typeGuard":"fn mcp_servers_is_table(root: &toml::Value) -> bool {\n    root.get(\"mcp_servers\").map(|v| v.is_table()).unwrap_or(true)\n}","tryCatchPattern":"if let Err(e) = apply_import(items, &path) {\n    if e.to_string().contains(\"[mcp_servers] is not a table\") {\n        eprintln!(\"Restore mcp_servers to a table section, then retry the import\");\n    } else {\n        return Err(e.into());\n    }\n}","preventionTips":["Keep all server definitions nested under [mcp_servers.<name>].","Never assign a scalar or string to mcp_servers.","Validate the section type after manual edits."],"tags":["toml","config","schema","mcp"],"backgroundTag":"invalid-toml-config","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}