Kuberwastaken/claurst · error · anyhow::Error
Failed to parse settings file
Error message
Failed to parse settings file {}: {}. The file was not modified; fix the JSON and restart Claurst. What it means
Claurst could not deserialize the user's settings.json file with serde_json. Because the file cannot be parsed, Claurst refuses to proceed (and never auto-modifies the file) so the user does not lose their configuration. The error message embeds the file path and the serde_json detail (e.g. 'expected value at line 3 column 5') to pinpoint the syntax problem.
Solutions
- Open the file printed in the error, fix the JSON at the line/column given in the serde detail, and restart Claurst — Claurst never modifies the file itself.
- Validate the file with `jq . settings.json` (or any JSON linter) to find the exact syntax problem before restarting.
- If the expected value shape changed (e.g. an enum string), check current docs for valid values for that field.
- As a last resort, back up settings.json, remove/rename it so Claurst regenerates defaults, and re-apply settings incrementally.
Example fix
// before (invalid JSON: trailing comma)
{
"theme": "dark",
}
// after
{
"theme": "dark"
} Defensive patterns
Strategy: validation
Validate before calling
// Rust
fn settings_json_valid(path: &std::path::Path) -> bool {
std::fs::read_to_string(path)
.map(|c| serde_json::from_str::<serde_json::Value>(&c).is_ok())
.unwrap_or(false)
}
// call before starting Claurst / relying on settings:
// if !settings_json_valid(&Settings::global_settings_path()) { /* repair first */ } Type guard
fn is_valid_settings(path: &Path) -> bool {
std::fs::read_to_string(path)
.map(|c| serde_json::from_str::<serde_json::Value>(&c).is_ok())
.unwrap_or(false)
} Try / catch
match Settings::load() {
Ok(s) => s,
Err(e) => { eprintln!("{e:#}"); eprintln!("Fix settings.json manually, then restart."); std::process::exit(1); }
} Prevention
- Validate settings.json with a JSON linter (`jq .`) after every manual edit.
- Use a JSON-schema-aware editor for settings.json to catch trailing commas and type errors as you type.
- Never hand-write JSON from memory — copy a known-good example and modify it.
- Keep a backup of a known-good settings.json for quick restore.
When it happens
Trigger: Settings load calls `Settings::parse_file(content, path)` on the contents of the global settings.json (`Settings::global_settings_path()` = config_dir().join("settings.json")). Any invalid JSON — trailing commas, single quotes instead of double quotes, comments, unquoted keys, truncated file, or a valid-JSON value with the wrong shape (e.g. a string where an object/enum is expected) — triggers this.
Common situations: Hand-editing settings.json and leaving a trailing comma; an editor or script writing JSONC-style comments; a crashed write leaving a truncated file; a settings field renamed across versions so the old value no longer matches the expected enum/shape.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- source settings.json must be a JSON object
- Refusing to overwrite malformed settings file
- Bridge poll: auth error
- Bridge session registration failed: authentication error
- No access_token in response
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/208fe6afc3f989af.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/core/src/lib.rs:1715
// 3. XDG config location for fresh installs.
if let Some(xdg) = std::env::var_os("XDG_CONFIG_HOME") {
let xdg = PathBuf::from(xdg);
// Per the XDG spec a relative $XDG_CONFIG_HOME must be ignored.
if xdg.is_absolute() {
return xdg.join("claurst");
}
}
home.join(".config").join("claurst")
}
/// Full path to the global settings JSON file.
pub fn global_settings_path() -> PathBuf {
Self::config_dir().join("settings.json")
}
fn parse_file(content: &str, path: &Path) -> anyhow::Result<Self> {
serde_json::from_str(content).map_err(|error| {
anyhow::anyhow!(
"Failed to parse settings file {}: {}. The file was not modified; fix the JSON and restart Claurst.",
path.display(),
error
)
})
}
async fn load_from_path(path: &Path) -> anyhow::Result<Self> {
if path.exists() {
let content = tokio::fs::read_to_string(path).await?;
Self::parse_file(&content, path)
} else {
Ok(Self::default())
}
}
fn load_from_path_sync(path: &Path) -> anyhow::Result<Self> {
if path.exists() {View on GitHub (pinned to b0637c97ec)