CLAUDE_PLUGIN_ROOT env var not always set when invoking plugin hooks
Bug Description
Plugin hooks configured in ~/.claude/settings.json that reference ${CLAUDE_PLUGIN_ROOT} intermittently fail with MODULE_NOT_FOUND because the CLAUDE_PLUGIN_ROOT environment variable is not always set by the Claude Code runtime when invoking hook commands.
Reproduction
- Install the
everything-claude-codeplugin (v1.9.0) - The plugin registers hooks in
~/.claude/settings.jsonusing${CLAUDE_PLUGIN_ROOT}to reference script paths, e.g.:
``json``
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/run-with-flags.js\" \"post:bash:pr-created\" \"scripts/hooks/post-bash-pr-created.js\" \"standard,strict\""
}]
}
- During a session,
PostToolUse:Bash hook errormessages appear intermittently
Root Cause
When CLAUDE_PLUGIN_ROOT is unset or empty, the shell expands ${CLAUDE_PLUGIN_ROOT} to an empty string, making the command:
node "/scripts/hooks/run-with-flags.js" ...
This is an absolute path that doesn't exist, causing Error: Cannot find module '/scripts/hooks/run-with-flags.js'.
Verified by reproducing manually:
unset CLAUDE_PLUGIN_ROOT
eval 'node "${CLAUDE_PLUGIN_ROOT}/scripts/hooks/run-with-flags.js" ...'
# Error: Cannot find module '/scripts/hooks/run-with-flags.js'
Evidence the Plugin Authors Know About This
The Stop and SessionEnd hooks in the same plugin use a much more robust inline Node.js resolver that tries multiple fallback paths (plugins/cache/, plugins/marketplace/, etc.) instead of relying on the env var. This workaround only covers those two hook types — all PreToolUse, PostToolUse, PreCompact, and SessionStart hooks are unprotected.
Expected Behavior
CLAUDE_PLUGIN_ROOT should always be set to the plugin's installation directory when Claude Code invokes any hook command from that plugin's configuration.
Workaround
Added shell default expansion in settings.json:
${CLAUDE_PLUGIN_ROOT:-/path/to/actual/plugin/cache/dir}
This uses the env var when available and falls back to the hardcoded path when it's missing.
Environment
- Claude Code: latest (as of 2026-04-02)
- Plugin: everything-claude-code v1.9.0
- OS: macOS (Darwin 25.4.0)
- Node: v25.8.1
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗