[BUG] WebFetch/WebSearch in permissions.deny Breaks Plugin Loading

Resolved 💬 3 comments Opened Nov 17, 2025 by Bwvolleyball Closed Jan 19, 2026

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?

Adding WebFetch(**) or WebSearch(**) to permissions.deny in settings.json causes all plugins to fail loading, resulting in "plugins": [] being empty in the init message even when plugins are explicitly enabled.

Affected Version: Claude Code v2.0.42

Impact: Critical - plugins completely fail to load, breaking any plugin-based functionality including hooks.

What Should Happen?

Adding WebFetch(**) or WebSearch(**) to permissions.deny should:

  1. Block WebFetch/WebSearch tool usage
  2. NOT affect plugin loading
  3. Plugins should still appear in "plugins": [...] array in init message

Error Messages/Logs

Steps to Reproduce

Minimal Reproduction

  1. Create a minimal settings.json with WebFetch or WebSearch in deny:
{
  "enabledPlugins": {
    "my-plugin@my-marketplace": true
  },
  "permissions": {
    "defaultMode": "acceptEdits",
    "deny": [
      "WebFetch(**)"
    ]
  }
}
  1. Run Claude Code with this settings file:
claude --settings settings.json --print --verbose --debug --output-format stream-json "test"
  1. Check the init message in output

Expected: "plugins": [{"name": "my-plugin", "path": "..."}]

Actual: "plugins": [] (empty array)

Confirmed Working Configurations

These configurations successfully load plugins:

// Works: Bash patterns in deny
{
  "enabledPlugins": {"my-plugin@marketplace": true},
  "permissions": {
    "deny": ["Bash(rm:*)", "Bash(git push:*)"]
  }
}

// Works: Read patterns in deny
{
  "enabledPlugins": {"my-plugin@marketplace": true},
  "permissions": {
    "deny": ["Read(.env)", "Read(**/.env)"]
  }
}

// Works: Empty deny list
{
  "enabledPlugins": {"my-plugin@marketplace": true},
  "permissions": {
    "deny": []
  }
}

Confirmed Broken Configurations

These configurations break plugin loading (plugins:[] empty):

// Breaks: WebFetch in deny
{
  "enabledPlugins": {"my-plugin@marketplace": true},
  "permissions": {
    "deny": ["WebFetch(**)"]
  }
}

// Breaks: WebSearch in deny
{
  "enabledPlugins": {"my-plugin@marketplace": true},
  "permissions": {
    "deny": ["WebSearch(**)"]
  }
}

// Breaks: Both in deny
{
  "enabledPlugins": {"my-plugin@marketplace": true},
  "permissions": {
    "deny": ["WebFetch(**)", "WebSearch(**)"]
  }
}

Test Evidence

Through systematic testing of 25 different configurations, we isolated:

| Configuration | Deny Patterns | Plugins Load? |
|--------------|--------------|---------------|
| Baseline | (none) | ✅ Yes |
| Bash patterns | Bash(rm:*), Bash(git push:*) | ✅ Yes |
| Read patterns | Read(.env), Read(**/.env) | ✅ Yes |
| WebFetch | WebFetch(**) | ❌ No |
| WebSearch | WebSearch(**) | ❌ No |
| Both Web tools | WebFetch(**), WebSearch(**) | ❌ No |

Key Finding: The bug is specific to WebFetch and WebSearch patterns. All other tool deny patterns work correctly.

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.0.42

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

iTerm2

Additional Information

Workaround

Since permissions.deny breaks plugins, use a PreToolUse hook instead:

Plugin structure:

block-web-plugin/
├── hooks/hooks.json
└── scripts/block-web.sh

hooks/hooks.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "WebFetch|WebSearch",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/block-web.sh"
          }
        ]
      }
    ]
  }
}

scripts/block-web.sh:

#!/usr/bin/env bash
set -euo pipefail

cat >&2 <<EOF
BLOCKED: Web access is not allowed.
Please use local files and tools instead.
EOF

exit 2

Enable in settings.json:

{
  "enabledPlugins": {
    "block-web-plugin@my-marketplace": true
  },
  "permissions": {
    "deny": [
      "Bash(rm:*)",
      "Read(.env)"
      // DO NOT include "WebFetch(**)" or "WebSearch(**)"
    ]
  }
}

This workaround:

  • ✅ Successfully loads plugins
  • ✅ Effectively blocks WebFetch/WebSearch via hook
  • ✅ Provides better error messages than permissions.deny
  • ✅ Avoids the bug entirely

Environment

  • Claude Code Version: v2.0.42
  • Platform: macOS (also reproduced in Docker Linux)
  • Settings Source: --settings CLI flag
  • Plugin System: Custom marketplace with local plugins

Related Issues

This may be related to:

  • Issue #11544 - Hooks not loading from settings.json in v2.0.37
  • Issue #8264 - --settings flag bypasses hooks

Both issues describe problems with hooks/plugins not loading when using settings.json, suggesting a deeper issue with the settings.json processing for plugin/hook configurations.

Suggested Fix

The permissions.deny processing appears to have special-case handling for WebFetch/WebSearch that incorrectly short-circuits plugin initialization. Recommend:

  1. Review permission processing code for WebFetch/WebSearch patterns
  2. Ensure plugin loading occurs before/independent of deny list processing
  3. Add test coverage for plugins + various deny patterns combinations
  4. Consider why WebFetch/WebSearch are treated differently than other tools

Request for Maintainers

  1. Can you confirm this bug exists in v2.0.42?
  2. Is there a specific reason WebFetch/WebSearch are handled differently?
  3. Should we expect a fix, or is the hook-based workaround the recommended approach?
  4. Are there other tool patterns that might have similar issues?

---

Note: This bug was discovered through systematic testing of 25 different configuration combinations to isolate the root cause. All test configurations and scripts are available if needed for further investigation.

View original on GitHub ↗

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