CLAUDE_PLUGIN_ROOT env var not always set when invoking plugin hooks

Resolved 💬 2 comments Opened Apr 2, 2026 by AdrianGrigorica Closed May 31, 2026

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

  1. Install the everything-claude-code plugin (v1.9.0)
  2. The plugin registers hooks in ~/.claude/settings.json using ${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\""
}]
}
``

  1. During a session, PostToolUse:Bash hook error messages 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

View original on GitHub ↗

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