Last week I installed a guard mod. It was supposed to hold risky shell commands, rm -rf, force pushes, the usual suspects, and show me what they would change before letting them run.
It was listed in /plugin. It was enabled. It did absolutely nothing.
I spent an embarrassing amount of time re-reading my hook code. The code was fine. The harness had quietly discarded my mod before a single event ever reached it, and it never told me in any place I was looking.
If your mod is "working" but silent, run this checklist first. In order of likelihood, none of these is your code.
Mistake 1: the project was never trusted
This one got me first. Claude Code only adopts a plugin from a project's .claude/ folder after you accept the trust dialog, the record lives in ~/.claude.json under projects[<path>].hasTrustDialogAccepted. Until then, an untrusted folder's .claude/ is repository content as far as the engine is concerned. It is not read at all.
The nasty variant: claude -p never shows the trust prompt. A headless run in a fresh folder will never load your project mod, and it will not complain about it either.
Two fixes, pick one:
- Open
claudeinteractively in the folder and accept the prompt. - Load it explicitly with
--plugin-dir .claude/skills/{name}, which loads it as{name}@inlinewithout needing trust.
User-level plugins under ~/.claude/skills/{name}/ always load. If your mod works from the user folder but not from the project folder, trust is your answer.
Mistake 2: I was looking in the wrong place
My mod logged everything through $.ui.log. In an interactive session, that appends a dim line to the transcript. In claude -p or the Agent SDK, there is no transcript and no status row, so those lines go exactly one place: the debug log.
The debug log lives at ~/.claude/debug/<session-id>.txt. Note the extension: .txt, not .log. There is a latest symlink pointing at the newest one, which saves you from guessing session ids.
The transcript also carries a dim line naming the mod and the reason when something fails to load. And claude --debug logs the load sequence itself. I now run every mod debugging session with --debug from the start. It is the difference between guessing and reading.
Also worth knowing: a mod that draws UI can check which surface it is on. Drawing only happens in the terminal and the Desktop app's Code tab. In the VS Code chat panel, claude -p, or a cloud session, your hooks run but your panes and bands simply do not exist. If your mod is a UI mod and you tested it headless, "doing nothing" is the correct behavior.
Mistake 3: my options were under the wrong key
Plugin options are read from user or managed settings under pluginConfigs, keyed by the plugin's full id, and the full id depends on how the plugin loaded:
- Auto-loaded from
.claude/skills/: the key is"{name}@skills-dir" - Loaded with
--plugin-dir: the key is"{name}"
I had mine under the bare name while the mod loaded as {name}@skills-dir. Every option silently fell back to its default. No error, no warning in the session. The debug log does say plugin {name}: no pluginConfigs["{name}@skills-dir"].options in user, --settings or managed settings, which is precise and completely invisible unless you are reading the log (see mistake 2).
One more trap inside this trap: options are read from user and managed settings, never from project settings. Putting them in the project's .claude/settings.json is the same as not setting them.
The fix looks like this in ~/.claude/settings.json:
{
"pluginConfigs": {
"my-guard@skills-dir": {
"options": {
"blockPatterns": ["rm -rf", "git push --force"]
}
}
}
}
Get the key wrong and you get defaults. Get the file wrong and you get defaults. The mod will not tell you.
Mistake 4: something turned the whole thing off
During early access, mods sat behind a rollout flag, and with the flag off the debug log said installed plugins' hooks modules not loaded: rollout flag is off. In 2.1.287 mods are on by default, so this specific trap is mostly history. But its modern equivalents are alive and well:
-
"disableAllHooks": truein~/.claude/settings.jsonstops your mods (and your settings hooks, and your custom status line) in every session. - An organization's
allowManagedModsOnlystops user-installed mods from loading while the rest of the plugin keeps working. Your skills and commands load fine, your mod does not, and nothing in the session explains the difference. - Anthropic can also turn off specific built-in pieces remotely; the plugin-authoring skill that lets Claude write mods is subject to that.
The cruel detail: built-in mods load regardless of these flags. So /plugin can show mods active, your session can look completely normal, and your mod is still not among them.
The checklist, in order
Before you touch your module code:
- Run
/pluginin an interactive session. Look for the dim line:N mod active ยท name. If yours is not named there, it never loaded. Stop debugging code. - Trust the project, or load with
--plugin-dir. - Read the debug log at
~/.claude/debug/latest(remember:.txt). Search for your plugin's name and forpluginConfigs. - Check your options key against the full plugin id, in user settings, not project settings.
- Check
disableAllHooksand any managed settings your org deploys.
Four of my last five "broken" mods were fixed by this list without changing a line of module code. The fifth was a typo in hooks.json, which claude plugin validate caught in three seconds. Run that too. Always run that.
What is the most silent failure you have hit with Claude Code mods so far? I have a feeling mistake 3 has bitten more people than will admit it.
Top comments (0)