nikivdev/code · error

expected [{}] to be a table in global flow config

Error message

expected [{}] to be a table in global flow config

What it means

When Flow edits the global TOML config, it ensures a given key holds a table, inserting a new table only if the key is absent. If the key exists but its value is NOT a table (e.g. a string, integer, or array), the function refuses to overwrite the existing value and bails; the same message is returned if the value cannot be borrowed as a mutable table.

Source

Thrown at src/ai.rs:9481

        return Ok(toml::value::Table::new());
    }

    let value: TomlValue =
        toml::from_str(&content).with_context(|| format!("failed to parse {}", path.display()))?;
    value
        .as_table()
        .cloned()
        .ok_or_else(|| anyhow::anyhow!("global flow config must be a TOML table"))
}

fn ensure_toml_table<'a>(
    root: &'a mut toml::value::Table,
    key: &str,
) -> Result<&'a mut toml::value::Table> {
    let needs_insert = !matches!(root.get(key), Some(TomlValue::Table(_)));
    if needs_insert {
        if root.contains_key(key) {
            bail!("expected [{}] to be a table in global flow config", key);
        }
        root.insert(key.to_string(), TomlValue::Table(toml::value::Table::new()));
    }
    root.get_mut(key)
        .and_then(TomlValue::as_table_mut)
        .ok_or_else(|| anyhow::anyhow!("expected [{}] to be a table in global flow config", key))
}

fn write_string_atomically(path: &Path, content: &str) -> Result<()> {
    let parent = path
        .parent()
        .ok_or_else(|| anyhow::anyhow!("missing parent for {}", path.display()))?;
    fs::create_dir_all(parent)?;
    let temp = parent.join(format!(
        ".{}.tmp-{}-{}",
        path.file_name()
            .and_then(|value| value.to_str())
            .unwrap_or("flow.toml"),

View on GitHub (pinned to a747e741ae)

Solutions

  1. Open the global flow config file and change the offending key from a scalar/array to a table: `key = "x"` → `[key]\nfield = "x"`
  2. Or delete the conflicting key and re-run the command so it inserts a fresh empty table
  3. Back up the config first, then validate the TOML (e.g. with a TOML linter) before re-running

Example fix

// before (global flow config, invalid)
daemon = 3
// after
[daemon]
enabled = true
Defensive patterns

Strategy: validation

Validate before calling

// Shell/Rust: inspect the global config TOML before running commands that write sections
# fail if the target key is not a table
python3 -c "import tomllib,sys; c=tomllib.load(open('global-flow-config.toml','rb')); k='daemon'; assert not (k in c and not isinstance(c[k], dict)), f'{k} is not a table'"

Type guard

fn is_toml_table(v: &toml::Value) -> bool {
    matches!(v, toml::Value::Table(_))
}

Try / catch

match enable_global_flow_config(root, "daemon") {
    Ok(table) => { /* edit table */ }
    Err(e) if e.to_string().contains("to be a table in global flow config") => {
        let key = /* extract from message */;
        eprintln!("Fix [{}]: replace the scalar/array value with a [{}] section in the global config", key, key);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Running a command that writes a `[section]` into the global flow config (e.g. enabling Codex global settings) while the global config file already defines that key as a scalar or array — e.g. `daemon = 3` instead of `[daemon]`.

Common situations: Hand-edited ~/.config/flow config where a section was written as a plain key/value; an older config format or migration left a scalar where the new code expects a table; automated scripts appending settings that collide with table keys.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/ec84203b77529e0a. Report an issue: GitHub.