Plugin SessionStart hooks fail — ${CLAUDE_PLUGIN_ROOT} not expanded for bash commands

Resolved 💬 2 comments Opened Mar 26, 2026 by kianwoon Closed Mar 27, 2026

Bug Description

Plugin SessionStart hooks that use ${CLAUDE_PLUGIN_ROOT} in bash commands fail on fresh session start. The environment variable is not being expanded, resulting in the hook trying to execute from the filesystem root.

Environment

  • Claude Code: 2.1.84
  • OS: macOS (Darwin 25.4.0)
  • Plugins affected: explanatory-output-style@claude-plugins-official, learning-output-style@claude-plugins-official

Steps to Reproduce

  1. Install explanatory-output-style and learning-output-style from claude-plugins-official
  2. Start a fresh Claude Code session (claude)
  3. Observe SessionStart:startup hook error messages

Error

Both plugins' hooks.json contain:

{
  "hooks": [{
    "type": "command",
    "command": "${CLAUDE_PLUGIN_ROOT}/hooks-handlers/session-start.sh"
  }]
}

When executed, produces:

bash: /hooks-handlers/session-start.sh: No such file or directory

${CLAUDE_PLUGIN_ROOT} resolves to empty, so the path becomes /hooks-handlers/session-start.sh instead of the full plugin cache path.

Other plugins work fine

The superpowers plugin also uses ${CLAUDE_PLUGIN_ROOT} in its SessionStart hook but works correctly because it uses bash -c '"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd" session-start' (quoted expansion).

The context-mode plugin uses node ${CLAUDE_PLUGIN_ROOT}/hooks/sessionstart.mjs and also works fine.

Workaround

Disabled both affected plugins. The issue appears specific to how ${CLAUDE_PLUGIN_ROOT} is expanded (or not) for bash shell commands in certain plugin hook configurations.

Expected Behavior

${CLAUDE_PLUGIN_ROOT} should be expanded to the plugin's cache path before the hook command is executed, regardless of whether the command uses node, bash, or other shell execution.

View original on GitHub ↗

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