{"record":{"id":"9a0958ad799dbee0","repo":"xai-org/grok-build","slug":"refusing-to-overwrite-unparseable-save-a-b","errorCode":null,"errorMessage":"refusing to overwrite unparseable {}: {}; save a backup and fix the syntax error before retrying","messagePattern":"refusing to overwrite unparseable (.+?): (.+?); save a backup and fix the syntax error before retrying","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/codegen/xai-grok-shell/src/util/config/persist.rs","lineNumber":27,"sourceCode":"/// Serializes the read-modify-write in `save_config` so two rapid\n/// settings toggles can't interleave and clobber each other.\nstatic SAVE_LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(());\n/// Blank (first-run 0-byte file) is an empty table; other unparseable TOML is\n/// an error so a silent fallback cannot drop unmodeled sections.\npub(crate) fn parse_existing_config_toml(s: &str) -> Result<TomlValue, toml::de::Error> {\n    if s.trim().is_empty() {\n        return Ok(TomlValue::Table(TomlMap::new()));\n    }\n    toml::from_str(s)\n}\n/// [`save_config`] body; caller must hold [`SAVE_LOCK`].\nasync fn save_config_locked(config: &Config) -> Result<()> {\n    let path = user_config_path();\n    let mut root: TomlValue = match tokio::fs::read_to_string(&path).await {\n        Ok(s) => match parse_existing_config_toml(&s) {\n            Ok(v) => v,\n            Err(parse_err) => {\n                return Err(anyhow::anyhow!(\n                    \"refusing to overwrite unparseable {}: {}; save a backup \\\n                         and fix the syntax error before retrying\",\n                    path.display(),\n                    parse_err,\n                ));\n            }\n        },\n        Err(_) => TomlValue::Table(TomlMap::new()),\n    };\n    if !matches!(root, TomlValue::Table(_)) {\n        root = TomlValue::Table(TomlMap::new());\n    }\n    let table = root.as_table_mut().expect(\"root must be a table\");\n    merge_section(table, \"cli\", &config.cli);\n    merge_section(table, \"models\", &config.models);\n    merge_section(table, \"ui\", &config.ui);\n    merge_section(table, \"harness\", &config.harness);\n    merge_section(table, \"session\", &config.session);","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/util/config/persist.rs#L9-L45","documentation":"save_config_locked refuses to write a new config over a file it cannot parse. Before saving, it re-reads user_config_path() and parses with parse_existing_config_toml; if parsing fails it aborts with this error rather than destroying the user's (possibly hand-modified) config with a syntax error. The message embeds the path and the parse error and asks the user to keep a backup and fix the syntax first.","triggerScenarios":"Calling update_config while the existing user config TOML on disk has a parse error (malformed TOML, invalid types in an already-passed section, stray characters), so the preservation of existing content cannot be guaranteed.","commonSituations":"A manual edit to config.toml introduced a typo; a comment containing an unescaped control character; a merge tool or script mangled the file; encoding issues (non-UTF8 bytes) after an editor or tool rewrote it.","solutions":["Read the embedded parse_err in the message to find the file/line of the syntax error","Copy the broken file to a backup (e.g. config.toml.bak) as the message instructs","Fix the TOML syntax error at the reported location with an editor or `tomlcheck`","Re-run update_config once the file parses cleanly"],"exampleFix":"# before (broken)\nmodel = \"grok-4\nmax_tokens = 4096\n# after\nmodel = \"grok-4\"\nmax_tokens = 4096","handlingStrategy":"validation","validationCode":"let s = tokio::fs::read_to_string(user_config_path()).await?;\nif let Err(e) = s.parse::<toml::Value>() {\n    eprintln!(\"config unparseable: {e}; fix syntax before updating config\");\n}","typeGuard":"fn parse_ok(s: &str) -> bool { s.parse::<toml::Value>().is_ok() }","tryCatchPattern":"match update_config(|cfg| { /* mutation */ }).await {\n    Err(e) if e.to_string().contains(\"refusing to overwrite unparseable\") => {\n        std::fs::copy(&cfg_path, \"config.toml.bak\")?;\n        eprintln!(\"backed up broken config; fix the TOML syntax error and retry\");\n    }\n    other => other,\n}","preventionTips":["Validate config.toml after every manual edit (tomlcheck / parse test)","Keep backups before editing config by hand","Never force-overwrite a config file that fails to parse","Add CI checks that parse the shipped/default config"],"tags":["toml","config","parse-error","data-loss-prevention"],"backgroundTag":"refusing-to-overwrite-unparseable-config","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}