Hmbown/CodeWhale · error

`config set` does not support nested key

Error message

`config set` does not support nested key `{key}`; edit `{field}` in the [{table}] table of config.toml instead (use a TOML value of the documented type). No value was changed.

What it means

`config set` only supports flat, scalar keys. Any dotted key other than the specially-handled mcp.server_definitions is treated as nested TOML and rejected, telling the user which table and field to edit by hand in config.toml. Nothing is written when this fires.

Solutions

  1. Edit config.toml directly: set `field` inside the `[table]` section with a correctly typed TOML value
  2. Use the dedicated subcommand for nested features where one exists (e.g. MCP server management)
  3. For mcp.server_definitions specifically, pass JSON text — it is persisted as a literal string, not TOML nesting

Example fix

// before
codewhale config set notifications.slack.webhook_url https://hooks.example/T123
// after
# edit config.toml:
[notifications.slack]
webhook_url = "https://hooks.example/T123"
Defensive patterns

Strategy: validation

Validate before calling

if key.contains('.') && key != "mcp.server_definitions" {
    let (table, field) = key.rsplit_once('.').unwrap();
    return Err(format!("edit `{field}` in the [{table}] table of config.toml directly"));
}

Try / catch

match config.set(key, value) {
    Err(e) if e.to_string().contains("does not support nested key") => {
        eprintln!("{e}"); // guidance names the exact table/field to edit
    }
    other => other?,
}

Prevention

When it happens

Trigger: `codewhale config set <table>.<field> <value>` for any nested setting except mcp.server_definitions — e.g. `config set notifications.slack.webhook_url https://...` or `config set mcp.foo.bar 1`.

Common situations: Scripting nested configuration; assuming `config set` mirrors every config.toml key; trying to set a table-valued or deeply nested option from the CLI.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/daabe118add8ca78. Report an issue: GitHub.

Appendix: source

Thrown at crates/config/src/lib.rs:2993

            "telemetry" => {
                self.telemetry = Some(parse_bool(value)?);
            }
            // Scheme rules (HTTPS, or loopback HTTP) are enforced where a
            // batch would actually be sent, not here: a user must be able to
            // stage a value before the machinery that reads it exists.
            "telemetry_endpoint" => self.telemetry_endpoint = Some(value.to_string()),
            "approval_policy" => self.approval_policy = Some(value.to_string()),
            "sandbox_mode" => self.sandbox_mode = Some(value.to_string()),
            "hook_sinks.unix_socket_path" => {
                self.hook_sinks
                    .get_or_insert_with(HookSinksToml::default)
                    .unix_socket_path = Some(PathBuf::from(value));
            }
            // The MCP stdio dispatcher persists this established literal key
            // as JSON text; it is not a nested TOML setting.
            _ if key.contains('.') && key != "mcp.server_definitions" => {
                let (table, field) = key.rsplit_once('.').expect("dotted key");
                bail!(
                    "`config set` does not support nested key `{key}`; edit `{field}` in the [{table}] table of config.toml instead (use a TOML value of the documented type). No value was changed."
                );
            }
            _ => {
                self.extras
                    .insert(key.to_string(), toml::Value::String(value.to_string()));
            }
        }
        Ok(())
    }

    pub fn unset_value(&mut self, key: &str) -> Result<()> {
        if notifications::in_namespace(key) {
            let setting = notifications::NotificationSetting::required(key)?;
            return notifications::edit_extras(&mut self.extras, setting, None);
        }
        if let Some((provider_id, model)) = parse_model_context_window_key(key) {
            self.unset_model_context_window(provider_id, model);

View on GitHub (pinned to 73e0f67d83)