Claude Code Plugin Bug: Hooks Warning & Marketplace Issues
Hey guys, let's dive into a frustrating bug I found while working with Claude Code plugins, particularly those installed through the marketplace. The issue revolves around a misleading warning about duplicate hook files. This causes a lot of headaches for plugin developers. I'll break down the problem, the steps to reproduce it, and why it's a big deal. Hopefully, this will help anyone else encountering the same issue.
The Core Problem: Misleading 'Duplicate Hooks' Warning
The central issue lies in Claude Code's handling of plugin hooks, specifically when plugins are installed from the marketplace. Claude Code displays a warning message that reads something like, "Duplicate hooks file detected: ./hooks/hooks.json resolves to an already-loaded file." This warning suggests that the hooks/hooks.json file is automatically loaded and that including it in the plugin's manifest (plugin.json) is redundant, leading to a conflict.
However, this warning is inaccurate for marketplace installations. In reality, for a plugin's hooks to function correctly when installed from the marketplace, you must explicitly include the line "hooks": "./hooks/hooks.json" in the plugin.json file. Without this, your plugin's hooks won't work. This creates a really annoying problem for plugin developers, because they often assume the warning is correct and they remove the hooks registration, which breaks the plugin silently!
This discrepancy creates a frustrating "ping-pong cycle" for plugin developers. They see the warning, assume the manifest registration is unnecessary, remove it, and then discover their hooks no longer work. They then re-add the registration, only to be confronted by the warning again. This is a lot of back and forth, wasting valuable development time and causing unnecessary confusion.
Detailed Explanation of the Bug
Let's unpack this in a more detailed manner, shall we? This bug essentially stems from how Claude Code handles the loading of hook files, specifically the hooks/hooks.json file, which is crucial for defining how your plugin interacts with Claude Code's internal processes. The issue manifests differently depending on how the plugin is installed.
When a plugin is loaded via the --plugin-dir flag (which specifies a directory containing plugins), Claude Code correctly auto-loads the hooks/hooks.json file. In this scenario, the warning about a duplicate hook file is accurate. Including the "hooks": "./hooks/hooks.json" line in the manifest would cause a duplicate load, and the warning serves its purpose.
The problems, however, begin with marketplace installations. Plugins distributed through the marketplace do not automatically load the hooks/hooks.json file. For hooks to function correctly, the plugin's plugin.json must contain the aforementioned line specifying the hook file's location. The warning message, however, makes no distinction between --plugin-dir and marketplace installations. So, plugin developers are misled into believing that this explicit registration is unnecessary, leading them to remove it. Because of this, their plugins' hooks cease to function, and the plugin becomes effectively broken.
To summarize: The current implementation incorrectly assumes a universal behavior across all installation methods. This results in the confusing and detrimental "duplicate hooks file" warning.
Steps to Reproduce the Bug
To make this super clear, I've included step-by-step instructions. These tests will show the difference in behavior between loading plugins from the --plugin-dir (where the warning is correct) and the marketplace (where it's wrong).
Test 1: Verifying --plugin-dir Auto-Loads Hooks
This test confirms that, when using the --plugin-dir flag, hooks are automatically loaded, and the warning is appropriate.
-
Create a Minimal Test Plugin: I'll create a simple test plugin without any explicit hook registration in the manifest file. We'll set up the plugin structure and define a hook within
hooks/hooks.json. This hook will trigger an action when a Bash command is used in the Claude Code environment. -
Plugin manifest The plugin's manifest is named
plugin.jsonand tells Claude Code about the plugin. It will have a basic name, version, and description. This ensures Claude Code recognizes our plugin and installs it correctly. -
Define Hook: The
hooks/hooks.jsonfile will contain the hook definition. This file specifies which events the plugin should hook into. We'll set up a hook that responds to thePreToolUseevent, which fires before a tool is used (in this case, before a Bash command). -
Run Claude with
--plugin-dir: We use the--plugin-dirflag to tell Claude Code where our plugin is located. This forces Claude Code to load the plugin. -
Test the Hook: In the Claude Code session, we run any Bash command (e.g.,
ls). Then, we check a specific file (e.g.,/tmp/hook-test-output.txt). If the hook worked, this file should exist with the text "HOOK FIRED."Result: File contains "HOOK FIRED" ✓ - Hooks DO auto-load for --plugin-dir
Test 2: Verifying Marketplace Does NOT Auto-Load Hooks
This test will show that when installing via the marketplace, the hooks do NOT automatically load unless explicitly registered in the manifest.
-
Add
marketplace.json: I'll add the necessarymarketplace.jsonfile. This is crucial for defining our plugin within the marketplace ecosystem. It describes the plugin's name, owner, version, and the location of the plugin's source code. -
Initialize as Git Repo: To simulate a real-world scenario, I'll initialize the test plugin directory as a Git repository. This lets me add, commit, and prepare the plugin for marketplace distribution.
-
Clean Up and Install via Marketplace: I'll remove the output file from the previous test to ensure a clean slate. Then, I'll use the
claude /plugin marketplace add /tmp/test-plugincommand to add the plugin to the marketplace. -
Restart Claude Code: This ensures that Claude Code recognizes the newly added plugin.
-
Run a Bash Command: I'll run a Bash command (e.g.,
ls) in Claude Code to trigger the hook. -
Test for Hook Activation: We'll check for the existence of
/tmp/hook-test-output.txtagain. If the hook worked, this file should exist, confirming that our hook was correctly triggered.Result: File does NOT exist ✗ - Hooks do NOT auto-load for marketplace installations
The Impact of the Bug and Workarounds
This bug has significant real-world implications, causing a frustrating development cycle. Plugin developers are caught in a "ping-pong cycle":
- See the duplicate warning.
- Remove the hooks registration in
plugin.json. - Hooks fail to fire (because of the removal).
- Re-add the registration.
- See the warning again, and the cycle repeats.
The recommended workaround is to ignore the warning and always include the "hooks": "./hooks/hooks.json" line in your plugin.json file for marketplace plugins. Also, it is a good idea to document this as a known false positive to prevent further confusion among your team members or other developers using your plugin. This workaround, while effective, isn't ideal because it requires developers to know about this specific bug and ignore a warning message, which can lead to unexpected behavior if not carefully managed.
Summary of the Problem
The issue is a context-dependent false warning about duplicate hook files. The warning accurately reflects behavior when using --plugin-dir but is incorrect for marketplace-installed plugins. This inconsistency leads to confusion and wasted development time, as developers are misled into removing necessary hook registrations.
Thanks for taking the time to read through this! Hopefully, this helps to clarify the situation, and Anthropic's team can take a look at the code and fix this confusing warning.