[BUG] Hooks Completely Non-Functional in Subdirectories (v2.0.27) - Blocking CI/CD Workflows

Resolved 💬 9 comments Opened Oct 26, 2025 by dcversus Closed Jan 12, 2026

[BUG] Hooks Completely Non-Functional in Subdirectories (v2.0.27) - Blocking CI/CD Workflows

Environment

  • Claude Code Version: v2.0.27
  • Platform: macOS Darwin 23.5.0
  • Test Directory: /tmp/test-project/workspace/subdir (subdirectory)
  • Settings Files Tested:
  • ~/.claude/settings.json (global)
  • /tmp/test-project/.claude/settings.json (project root)
  • /tmp/test-project/workspace/subdir/.claude/settings.json (working directory)

Problem Statement

All hook types (UserPromptSubmit, SessionStart, PreToolUse, PostToolUse, Stop) completely fail to execute when Claude Code runs from subdirectories. This blocks development of advanced CI/CD pipelines that rely on hooks to orchestrate automated workflows.

This appears to be a combination of Issue #8810 (subdirectory bug) and Issue #6305 (PreToolUse/PostToolUse broken), making hooks entirely unusable in real-world project structures where Claude Code is typically launched from nested directories.

Automated Reproduction Script

Run this script to reproduce the bug in a clean environment:

#!/usr/bin/env bash
# claude-hooks-reproduction-test.sh

set -e

echo "🧪 Claude Code Hooks Bug Reproduction Test"
echo "=========================================="
echo ""

# Setup test directory structure
TEST_DIR="/tmp/claude-hooks-test-$$"
mkdir -p "$TEST_DIR/workspace/subdir"
cd "$TEST_DIR"

# Create hook script
mkdir -p "$TEST_DIR/workspace/subdir/.claude/hooks"
cat > "$TEST_DIR/workspace/subdir/.claude/hooks/test-hook.sh" <<'HOOK_EOF'
#!/usr/bin/env bash
read -r input_json
echo "🎬 Hook executed! $(date)" >> /tmp/claude-hook-test.log
echo "🎬 Hook fired!" >&2
exit 0
HOOK_EOF

chmod +x "$TEST_DIR/workspace/subdir/.claude/hooks/test-hook.sh"

# Test 1: Local settings.json (working directory)
echo "Test 1: Local .claude/settings.json in working directory"
cat > "$TEST_DIR/workspace/subdir/.claude/settings.json" <<'SETTINGS_EOF'
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/tmp/claude-hooks-test-$$/workspace/subdir/.claude/hooks/test-hook.sh"
          }
        ]
      }
    ]
  }
}
SETTINGS_EOF

# Replace $$ with actual PID in the script path
sed -i '' "s/\\$\\$/$$/g" "$TEST_DIR/workspace/subdir/.claude/settings.json"

# Test 2: Global settings.json
echo "Test 2: Global ~/.claude/settings.json"
mkdir -p ~/.claude
cp ~/.claude/settings.json ~/.claude/settings.json.backup 2>/dev/null || true

cat > ~/.claude/settings.json <<GLOBAL_EOF
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$TEST_DIR/workspace/subdir/.claude/hooks/test-hook.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cat > /dev/null && date '+Stop: %Y-%m-%d %H:%M:%S' >> /tmp/claude-hook-test.log"
          }
        ]
      }
    ]
  }
}
GLOBAL_EOF

# Clear test log
rm -f /tmp/claude-hook-test.log

echo ""
echo "📁 Test environment created at: $TEST_DIR"
echo "📝 Hook script: $TEST_DIR/workspace/subdir/.claude/hooks/test-hook.sh"
echo "📝 Local settings: $TEST_DIR/workspace/subdir/.claude/settings.json"
echo "📝 Global settings: ~/.claude/settings.json"
echo ""
echo "✅ Manual hook test:"
echo '{"test": true}' | "$TEST_DIR/workspace/subdir/.claude/hooks/test-hook.sh" && echo "   Script works when run manually ✓"

echo ""
echo "🚀 Next steps:"
echo "1. cd $TEST_DIR/workspace/subdir"
echo "2. claude --dangerously-skip-permissions"
echo "3. Send any message to Claude"
echo "4. Ask Claude to run: cat /tmp/claude-hook-test.log"
echo ""
echo "📊 Expected: Hook log entries appear"
echo "📊 Actual: No log file created (hooks never fire)"
echo ""
echo "Cleanup: rm -rf $TEST_DIR && mv ~/.claude/settings.json.backup ~/.claude/settings.json 2>/dev/null || true"

Reproduction Steps

From any subdirectory:

  1. Run the reproduction script:

``bash
chmod +x claude-hooks-reproduction-test.sh
./claude-hooks-reproduction-test.sh
``

  1. Follow the instructions printed by the script:

``bash
cd /tmp/claude-hooks-test-XXXXX/workspace/subdir
claude --dangerously-skip-permissions
``

  1. Send any message to Claude
  1. Ask Claude: "Check if the hook log exists: cat /tmp/claude-hook-test.log"

Expected Result:

🎬 Hook executed! Sun Oct 26 08:30:15 WET 2025
Stop: 2025-10-26 08:30:16

Actual Result:

cat: /tmp/claude-hook-test.log: No such file or directory

---

Testing Evidence

✅ Manual Script Execution Works

$ echo '{"tool":"Bash"}' | /tmp/claude-hooks-test-12345/workspace/subdir/.claude/hooks/test-hook.sh
🎬 Hook fired!

$ cat /tmp/claude-hook-test.log
🎬 Hook executed! Sun Oct 26 08:30:15 WET 2025

❌ Claude Code Never Invokes Hooks

$ cat /tmp/claude-hook-test.log
cat: /tmp/claude-hook-test.log: No such file or directory

Despite 15+ restarts, /hooks command usage, and testing all hook types.

---

Workarounds Attempted

| Approach | Result |
|----------|--------|
| Absolute paths in hook commands | ❌ No execution |
| Symlink ~/.claude/settings.json to project .claude/ | ❌ No execution |
| Inline commands (no script files) | ❌ No execution |
| All hook types (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop) | ❌ None execute |
| /hooks command after every change | ❌ No change |
| Complete Claude Code restarts (15+) | ❌ No change |

---

Expected vs. Actual Behavior

According to docs:

User settings are defined in ~/.claude/settings.json and apply to all projects.

Expected:

  • Hooks in ~/.claude/settings.json execute regardless of cwd
  • Hooks in local .claude/settings.json execute when present
  • /hooks command reflects loaded hooks
  • Hooks fire after restart

Actual (in subdirectories):

  • Global hooks (~/.claude/settings.json) don't execute
  • Local hooks (.claude/settings.json) don't execute
  • /hooks command doesn't show configured hooks
  • No hook process spawned, no logs, no errors

Actual (from ~ directory):

  • Hooks reportedly work ~80-90% of the time (Issue #8810)
  • Duplicate firing reported (Issue #3465)

---

Impact Assessment

Severity: 🔴 CRITICAL

Blocked Use Cases:

  1. CI/CD Pipelines: Can't automate testing, validation, deployment
  2. Multi-Agent Workflows: Can't orchestrate parallel sessions
  3. Context Injection: Can't add project-specific context
  4. Security Validation: Can't enforce command approval
  5. Progress Tracking: Can't auto-update documentation
  6. Quality Gates: Can't block unsafe operations

Workaround: None. Only option is always launching from ~ (impractical).

---

Related Issues

  • #8810: UserPromptSubmit hooks not working in subdirectories
  • #6305: PreToolUse/PostToolUse hooks not executing
  • #3579: User settings hooks not loading
  • #2814: Hooks system configuration issues
  • #6403: PostToolUse hooks not executing despite stdin JSON
  • #3465: Duplicate hook firing from home directory

---

Proposed Solutions

Option 1: Fix Subdirectory Support (Immediate)

  1. Load ~/.claude/settings.json regardless of process.cwd()
  2. Resolve hook paths relative to settings file location
  3. Add logging: "Loaded N hooks from ~/.claude/settings.json"

Option 2: Debug Path Resolution (Root Cause)

  1. Verify ~/.claude/settings.json is read when cwd != ~
  2. Check working directory for hook spawning
  3. Log hook loading failures

Option 3: Add /hooks debug Command

$ claude /hooks debug
✅ Global settings: ~/.claude/settings.json (5 hooks loaded)
✅ Local settings: .claude/settings.json (3 hooks loaded)
❌ Hook execution: 0 hooks fired in session
⚠️ Subdirectory issue: known bug #8810

Option 4: Provide Source Code Access

For community debugging:

  • Settings loading logic
  • Hook spawning mechanism
  • Path resolution code

---

Request for Anthropic Team

  1. Acknowledge as critical bug affecting core feature
  2. Prioritize fix - hooks are advertised but broken
  3. Share debugging guidance - how can community help?
  4. Provide workaround - any way to make hooks work in subdirs?
  5. Update docs - clarify if "hooks only work from ~" is intended

---

Testing Offer

We're happy to:

  • Test patches before release
  • Provide detailed logging
  • Contribute documentation improvements
  • Share CI/CD workflow designs

---

Summary: Hooks are completely non-functional in subdirectories (v2.0.27), blocking CI/CD use cases. Combines bugs #8810 and #6305. Needs urgent fix.

View original on GitHub ↗

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