Kuberwastaken/claurst · error
source settings.json must be a JSON object
Error message
source settings.json must be a JSON object
What it means
Thrown by `map_settings_preview` (part of `prepare_import_with_paths`) when the source settings.json parsed as a serde_json Value but is not a JSON object. The import path requires the top-level source document to be an object so its fields can be iterated for the preview; arrays, strings, numbers, or null are rejected with this message.
Solutions
- Inspect the source file: ensure its top level is `{ ... }`, not an array or scalar.
- If it is a wrapped export, unwrap to the inner object before importing.
- Re-export settings from the source tool so settings.json is a proper object.
- In callers, validate `value.is_object()` before invoking the import/preview APIs.
Example fix
// before
let v: Value = serde_json::from_str(&raw)?;
prepare_import_with_paths(&v, ...)?; // v = [ ... ] -> error
// after
let v: Value = serde_json::from_str(&raw)?;
if !v.is_object() {
anyhow::bail!("settings source is not an object");
}
prepare_import_with_paths(&v, ...)?; Defensive patterns
Strategy: type-guard
Validate before calling
let v: serde_json::Value = serde_json::from_str(&raw)?;
if !v.is_object() {
anyhow::bail!("settings source is not a JSON object");
} Type guard
fn is_json_object(v: &serde_json::Value) -> bool { v.is_object() } Try / catch
match prepare_import_with_paths(&source, ...) {
Ok(preview) => apply(preview),
Err(e) if e.to_string().contains("must be a JSON object") => {
// inspect/repair the source file, then retry
inspect_and_fix_source(path)?;
}
Err(e) => return Err(e),
} Prevention
- Check the source file's top level is { ... } before importing.
- Unwrap wrapped exports (e.g. [{...}] or {"settings":{...}}) to the plain object.
- Validate with serde_json::Value::is_object at load time.
- Re-export settings from the source tool if the file looks corrupted.
When it happens
Trigger: Pointing the import command at a settings.json whose top level is an array, string, number, boolean, or null — e.g. a file containing `[...]`, a JSON-lines dump, or a wrongly exported settings file.
Common situations: Importing a file that is actually a JSON array of settings snapshots; importing an export log or wrapped payload like {"settings": {...}} saved incorrectly as `["{...}"]`; a corrupted/partially-written settings file; grabbing the wrong file (not settings.json) that still parses as JSON.
Related errors
- mcpServers must be an object
- mcpServers. must be an object
- hooks must be an object
- hooks. must be an array
- hooks. [] must be an object
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/b131595599a91d85.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/core/src/import_config.rs:307
struct SettingsPreviewOutcome {
preview_fields: Vec<PreviewField>,
imported_fields: Vec<String>,
skipped_fields: Vec<String>,
imported_count: usize,
replaced_count: usize,
kept_count: usize,
skipped_count: usize,
}
fn map_settings_preview(
source: &Value,
current: &Value,
target: &mut Settings,
) -> Result<SettingsPreviewOutcome> {
let source_obj = source
.as_object()
.ok_or_else(|| anyhow!("source settings.json must be a JSON object"))?;
let mut preview_fields = Vec::new();
let mut imported_fields = Vec::new();
let mut skipped_fields = Vec::new();
let mut imported_count = 0;
let mut replaced_count = 0;
let mut kept_count = 0;
let mut skipped_count = 0;
if source_obj.contains_key("model") {
preview_fields.push(PreviewField {
name: "model".to_string(),
action: PreviewAction::Skip,
reason: Some("model is not imported to keep the current session and default model unchanged".to_string()),
});
skipped_fields.push("model".to_string());
skipped_count += 1;
} else {View on GitHub (pinned to b0637c97ec)