Kuberwastaken/claurst · error · anyhow::Error

Refusing to overwrite malformed settings file

Error message

Refusing to overwrite malformed settings file {}: {}

What it means

The async settings save path (`save_to_path`) refuses to overwrite an existing settings file whose current contents do not parse as valid JSON. This is a safety guard: if the file is malformed (possibly due to a crash or user edit), rewriting it would silently destroy the user's configuration, so the save aborts instead.

Solutions

  1. Fix or remove the malformed settings.json at the path shown (back it up first), then retry the save.
  2. Validate the existing file with `jq . settings.json` to locate the syntax error and repair it before re-running the save.
  3. If the file is unrecoverable, delete it so the next save starts from a fresh valid file.

Example fix

cp settings.json settings.json.broken
jq . settings.json  # shows exact syntax error
echo '{}' > settings.json  # reset after backup if unrecoverable
Defensive patterns

Strategy: validation

Validate before calling

// before calling an async save, ensure the current file parses:
let path = Settings::global_settings_path();
if path.exists() {
    let content = tokio::fs::read_to_string(&path).await?;
    serde_json::from_str::<serde_json::Value>(&content)
        .map_err(|e| anyhow::anyhow!("settings.json is malformed, repair before saving: {e}"))?;
}

Type guard

fn settings_file_parseable(content: &str) -> bool {
    serde_json::from_str::<serde_json::Value>(content).is_ok()
}

Try / catch

match settings.save().await {
    Err(e) if e.to_string().contains("Refusing to overwrite malformed") => {
        eprintln!("{e:#}"); eprintln!("Repair or delete settings.json, then retry.");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling any async settings-save API (e.g. persisting updated settings) where `path.exists()` and the on-disk content fails `Settings::parse_file` — i.e. the existing settings.json is invalid JSON or has the wrong shape for the settings struct.

Common situations: The user hand-edited settings.json and broke the syntax, then the app or a TUI command tries to persist a setting; a previous write was truncated mid-flight leaving corrupt JSON; an external tool replaced the file with non-JSON content.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/0b32f2ae49d07284. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/core/src/lib.rs:1745

            } else {
                Ok(Self::default())
            }
        }

        fn load_from_path_sync(path: &Path) -> anyhow::Result<Self> {
            if path.exists() {
                let content = std::fs::read_to_string(path)?;
                Self::parse_file(&content, path)
            } else {
                Ok(Self::default())
            }
        }

        async fn save_to_path(&self, path: &Path) -> anyhow::Result<()> {
            if path.exists() {
                let content = tokio::fs::read_to_string(path).await?;
                Self::parse_file(&content, path).map_err(|error| {
                    anyhow::anyhow!(
                        "Refusing to overwrite malformed settings file {}: {}",
                        path.display(),
                        error
                    )
                })?;
            }
            if let Some(parent) = path.parent() {
                tokio::fs::create_dir_all(parent).await?;
            }
            let content = serde_json::to_string_pretty(self)?;
            tokio::fs::write(path, content).await?;
            Ok(())
        }

        fn save_to_path_sync(&self, path: &Path) -> anyhow::Result<()> {
            if path.exists() {
                let content = std::fs::read_to_string(path)?;
                Self::parse_file(&content, path).map_err(|error| {

View on GitHub (pinned to b0637c97ec)