{"record":{"id":"2093b598c05661ce","repo":"atuinsh/atuin","slug":"empty-config-key","errorCode":null,"errorMessage":"empty config key","messagePattern":"empty config key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin/src/command/client/config.rs","lineNumber":323,"sourceCode":"\n    if v.is_str() {\n        Some(ValueType::String)\n    } else if v.is_bool() {\n        Some(ValueType::Boolean)\n    } else if v.is_integer() {\n        Some(ValueType::Integer)\n    } else if v.is_float() {\n        Some(ValueType::Float)\n    } else {\n        None\n    }\n}\n\nfn set_deep_key(doc: &mut DocumentMut, key: &str, value: Value) -> Result<()> {\n    let parts: Vec<&str> = key.split('.').collect();\n\n    if parts.is_empty() {\n        eyre::bail!(\"empty config key\");\n    }\n\n    let mut current: &mut dyn TableLike = doc.as_table_mut();\n\n    // Navigate/create intermediate tables\n    for &part in &parts[..parts.len() - 1] {\n        if !current.contains_key(part) {\n            current.insert(part, Item::Table(Table::new()));\n        }\n        current = current\n            .get_mut(part)\n            .expect(\"just inserted or already exists\")\n            .as_table_like_mut()\n            .ok_or_else(|| eyre::eyre!(\"'{}' exists but is not a table\", part))?;\n    }\n\n    let last = *parts.last().unwrap();\n","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/atuinsh/atuin/blob/900007c48045867e7f44b0f9bc55ad9dbe99ee0d/crates/atuin/src/command/client/config.rs#L305-L341","documentation":"`set_deep_key` splits the config key on `.` and asserts at least one part exists before walking/creating nested TOML tables. Because `String::split('.')` on an empty string actually yields one empty part, this bail is effectively a defensive invariant guard for an empty/degenerate key reaching the function.","triggerScenarios":"Calling `set_deep_key` with an empty or all-dot key path (e.g. \"\" or \".\"), reached via `config set` internal paths or `get_updated_config` when the key argument degenerates to nothing.","commonSituations":"Programmatic/plugin use of the set path with a malformed key; edge cases where a key consisting only of dots produces only empty parts.","solutions":["Always pass a non-empty key with at least one non-empty segment: `atuin config set theme.name dark`.","Validate/trim the key before calling config-set logic.","Prefer single-level keys like `atuin config set auto_sync true` when no nesting is needed."],"exampleFix":"// before\nset_deep_key(doc, \"\", value)  // bails: empty config key\n// after\nset_deep_key(doc, \"theme.name\", value)","handlingStrategy":"validation","validationCode":"fn valid_key(k: &str) -> bool {\n    !k.is_empty() && k.split('.').all(|p| !p.is_empty())\n}","typeGuard":"fn valid_key(k: &str) -> bool {\n    !k.is_empty() && k.split('.').all(|p| !p.is_empty())\n}","tryCatchPattern":null,"preventionTips":["Never construct keys from unvalidated user input.","Reject keys that are empty or consist only of dots.","Split on '.' and assert every segment is non-empty before calling set logic."],"tags":["config","toml","internal"],"backgroundTag":"empty-required-field","analyzedSha":"900007c48045867e7f44b0f9bc55ad9dbe99ee0d","analyzedAt":"2026-09-19T12:52:51.491Z","contentChangedAt":"2026-09-19T12:52:51.491Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}