xai-org/grok-build · error
{}: hooks is not a JSON object
Error message
{}: hooks is not a JSON object What it means
After ensuring the root is an object, apply_hooks_to_dir fetches or creates the root's 'hooks' key and requires it to be a JSON object whose keys are event names. If 'hooks' exists but is an array, string, or other non-object, merging hook groups is impossible and this error is thrown.
Source
Thrown at crates/codegen/xai-grok-shell/src/claude_import.rs:1074
warn!(
path = %target.display(),
error = %e,
"Existing imported-from-claude.json is malformed; replacing with fresh content. \
Manual edits in the malformed file will be lost."
);
serde_json::json!({})
}),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => serde_json::json!({}),
Err(e) => return Err(e.into()),
};
let root_obj = root
.as_object_mut()
.ok_or_else(|| anyhow::anyhow!("{}: root is not a JSON object", target.display()))?;
let hooks_obj = root_obj
.entry("hooks".to_string())
.or_insert_with(|| serde_json::json!({}))
.as_object_mut()
.ok_or_else(|| anyhow::anyhow!("{}: hooks is not a JSON object", target.display()))?;
let mut count = 0usize;
// `dirty` tracks whether we mutated the JSON in any way (including
// in-place timeout refreshes that don't add new entries). The file is
// re-written iff dirty, even when count == 0.
let mut dirty = false;
for item in new_hooks {
let ImportableItem::Hook {
event,
matcher,
command,
timeout,
} = item
else {
continue;
};
let groups = hooks_objView on GitHub (pinned to bc7f02eddd)
Solutions
- Edit settings.json so "hooks" is an object keyed by event name, e.g. "hooks": {"PreToolUse": []}
- Remove the bad "hooks" key and re-run the import so it is recreated as {}
- Validate settings.json against the expected hooks schema before importing
Example fix
// before
{"hooks": []}
// after
{"hooks": {"PreToolUse": []}} Defensive patterns
Strategy: type-guard
Validate before calling
let v: serde_json::Value = serde_json::from_str(&raw)?;
if !v.get("hooks").map_or(true, |h| h.is_object()) {
return Err("hooks must be a JSON object".into());
} Type guard
fn hooks_is_object(v: &serde_json::Value) -> bool {
v.get("hooks").map_or(true, serde_json::Value::is_object)
} Try / catch
match apply_import(...) {
Err(e) if e.to_string().contains("hooks is not a JSON object") => {
// repair: force hooks to {}
let mut v: serde_json::Value = serde_json::from_str(&raw)?;
v["hooks"] = serde_json::json!({});
serde_json::to_writer_pretty(&mut file, &v)?;
apply_import(...) // retry
}
r => r,
} Prevention
- Keep hooks as an object keyed by event name
- Check settings.json shape after upgrading tool versions
- Validate against the hooks schema before import
When it happens
Trigger: apply_import encounters a settings.json where "hooks" is set to a non-object value, e.g. "hooks": [] or "hooks": "none".
Common situations: Hand-edited settings.json with wrong hooks shape; older config format that stored hooks as a list; copy-paste from outdated documentation.
Related errors
- {}: root is not a JSON object
- {}: hooks.{} is not a JSON array
- Failed to create agent config: {e}
- Invalid ACP content blocks: {e}
- Invalid ACP content blocks in "content": {e}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/e793678f59f3e961.
Report an issue: GitHub.