--settings flag merges instead of overriding hooks configuration

Resolved 💬 4 comments Opened Nov 11, 2025 by Elijas Closed Nov 12, 2025

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

When using the --settings flag with a custom settings JSON file, the hooks configuration merges with the existing ~/.claude/settings.json instead of overriding it. This makes it impossible to disable specific hooks (like Stop hooks) on a per-invocation basis.

Actual behavior:

  • Hooks from ~/.claude/settings.json are still executed even when providing --settings custom.json with empty or modified hooks arrays
  • The custom settings appear to merge/append rather than replace the original hooks

Impact:
This prevents use cases like:

  • Temporarily disabling notification/sound hooks for batch processing
  • Running Claude programmatically without triggering user-facing hooks
  • Testing different hook configurations without modifying the global settings file

What Should Happen?

The --settings flag should either:

Option A (Preferred): Completely override the default ~/.claude/settings.json for the current invocation

  • Custom settings file becomes the sole source of configuration
  • No merging with default settings
  • Clear documentation that --settings is a full override

Option B (Alternative): Provide a --no-hooks or --skip-hooks flag

  • Explicit way to disable hooks for specific invocations
  • Simpler than managing custom settings files
  • Useful for programmatic/batch usage

Error Messages/Logs

No error messages - the behavior is silent. The hooks from the default settings file simply execute despite the custom settings specifying empty hooks.

Steps to Reproduce

  1. Create a settings file at ~/.claude/settings.json with a Stop hook:
{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/some-notification-hook.sh"
          }
        ]
      }
    ]
  }
}
  1. Create a custom settings file at /tmp/custom-settings.json with empty Stop hooks:
{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": []
      }
    ]
  }
}
  1. Run a command with the custom settings:
claude --settings /tmp/custom-settings.json --print "Say hello"
  1. Expected: The notification hook should NOT execute (empty hooks array in custom settings)
  1. Actual: The notification hook DOES execute (hooks merged from both files)

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.0.37 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

Workaround

The only current workaround is to temporarily backup and modify ~/.claude/settings.json:

import shutil
from pathlib import Path

settings_path = Path.home() / ".claude" / "settings.json"
backup_path = Path.home() / ".claude" / "settings.json.backup"

# Backup original
shutil.copy2(settings_path, backup_path)

# Modify settings
# ... (remove unwanted hooks)

# Run claude command
subprocess.run(["claude", "--print", "prompt"])

# Restore original
shutil.copy2(backup_path, settings_path)
backup_path.unlink()

This is error-prone and requires careful cleanup even on exceptions/timeouts.

Documentation Gap

The current documentation for --settings doesn't clarify:

  • Whether settings merge or override
  • How nested structures (like hooks arrays) are handled
  • The precedence/priority of different configuration sources

Suggested Fix

If the merge behavior is intentional, consider adding:

  1. --settings-override flag for full replacement behavior
  2. --no-hooks flag for disabling all hooks
  3. --skip-hook <pattern> flag for selective hook disabling
  4. Clear documentation about merge vs override semantics

Use Case

This issue arose when building a YouTube transcript summarization tool that calls Claude programmatically. The tool needs to avoid triggering sound/notification hooks during batch processing, while preserving those hooks for interactive use.

---

Related: This may affect other nested configuration structures (MCP servers, tools, etc.) but hooks are the most critical for programmatic usage.

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗