Fixing Claude Code Hooks: The New Matcher Format
Claude Code recently changed their hooks format, and if you haven't updated your project settings, you'll see this cryptic error: hooks: Expected array, but received undefined Files with errors are...

Source: DEV Community
Claude Code recently changed their hooks format, and if you haven't updated your project settings, you'll see this cryptic error: hooks: Expected array, but received undefined Files with errors are skipped entirely, not just the invalid settings. The Problem The old hook format put command at the top level: { "hooks": { "Stop": [ { "matcher": "", "command": "bash .claude/hooks/my-script.sh" } ] } } This silently breaks - your entire settings file gets skipped. The Fix The new format requires a nested hooks array with explicit type: { "hooks": { "Stop": [ { "matcher": ".*", "hooks": [ { "type": "command", "command": "bash .claude/hooks/my-script.sh" } ] } ] } } Key changes: matcher - Must be a valid regex (".*" matches everything, empty string doesn't work) hooks - Now an array inside the matcher object type - Required field, set to "command" for shell commands Quick Migration Find all your .claude/settings.json files and update them: # Find all Claude settings files find ~/projects -na