SessionStart hooks (run-hook.cmd, session-start) fail when PATH is broken: dirname/cat/bash not found

Author: krisk248Created Sep 17, 2026Updated Sep 17, 2026

Summary

The SessionStart hook scripts (hooks/run-hook.cmd and hooks/session-start) call external commands (dirname, cat, and re-invoke bash via PATH lookup) that are not guaranteed to be resolvable in the environment Claude Code spawns hooks in. On a system where PATH is corrupted/empty at hook-spawn time (a known Claude Code issue — anthropics/claude-code#43127 and related), these hooks fail outright:

SessionStart:startup hook error
Failed with non-blocking status code: .../hooks/run-hook.cmd: line 43: dirname: command not found

and, after working around that one, the next external dependency surfaces:

SessionStart:startup hook error
Failed with non-blocking status code: .../hooks/session-start: line 43: cat: command not found

Since this is a SessionStart:startup hook — one of the very first things that runs — it appears to race ahead of any user-side PATH-repair SessionStart hooks (they run in the same startup batch, so a fix that backfills PATH for future subprocesses doesn't help a sibling hook in the same batch). So plugin hooks that rely on PATH at all are exposed to this a lot more than regular tool-invoked hooks.

Fix

None of these calls need an external binary — bash builtins cover all of it:

  • hooks/run-hook.cmd, hooks/session-start: SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"SCRIPT_DIR="$(cd "${0%/*}" && pwd)"
  • hooks/run-hook.cmd: exec bash "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"exec "${BASH:-bash}" "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@" (bash sets $BASH to its own absolute path, no PATH lookup needed)
  • hooks/session-start: cat "${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md"$(<"${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md") (builtin redirection read), guarded with [ -r ... ] instead of ... || echo "Error reading..."
  • hooks/session-start: the trailing | cat on each of the three printf branches is a no-op pass-through and can just be removed

I patched a local copy (plugin cache, v6.3.0) this way and verified both a normal run and a run under a completely empty PATH (env -i PATH= ... bash run-hook.cmd session-start) produce identical, correct JSON output.

Environment

  • Claude Code v2.1.274
  • superpowers plugin v6.3.0 (official marketplace)
  • Fedora Linux, zsh

Suggested patch

--- a/hooks/run-hook.cmd
+++ b/hooks/run-hook.cmd
@@
 # Unix: run the named script directly
-SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SCRIPT_DIR="$(cd "${0%/*}" && pwd)"
 SCRIPT_NAME="$1"
 shift
-exec bash "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"
+exec "${BASH:-bash}" "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"

--- a/hooks/session-start
+++ b/hooks/session-start
@@
 # Determine plugin root directory
-SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SCRIPT_DIR="$(cd "${0%/*}" && pwd)"
 PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

 # Read using-superpowers content
-using_superpowers_content=$(cat "${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md" 2>&1 || echo "Error reading using-superpowers skill")
+if [ -r "${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md" ]; then
+    using_superpowers_content=$(<"${PLUGIN_ROOT}/skills/using-superpowers/SKILL.md")
+else
+    using_superpowers_content="Error reading using-superpowers skill"
+fi
@@
 if [ -n "${CURSOR_PLUGIN_ROOT:-}" ]; then
   # Cursor sets CURSOR_PLUGIN_ROOT (may also set CLAUDE_PLUGIN_ROOT)
-  printf '{\n  "additional_context": "%s"\n}\n' "$session_context" | cat
+  printf '{\n  "additional_context": "%s"\n}\n' "$session_context"
 elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -z "${COPILOT_CLI:-}" ]; then
   # Claude Code sets CLAUDE_PLUGIN_ROOT without COPILOT_CLI
-  printf '{\n  "hookSpecificOutput": {\n    "hookEventName": "SessionStart",\n    "additionalContext": "%s"\n  }\n}\n' "$session_context" | cat
+  printf '{\n  "hookSpecificOutput": {\n    "hookEventName": "SessionStart",\n    "additionalContext": "%s"\n  }\n}\n' "$session_context"
 else
   # Copilot CLI (sets COPILOT_CLI=1) or unknown platform — SDK standard format
-  printf '{\n  "additionalContext": "%s"\n}\n' "$session_context" | cat
+  printf '{\n  "additionalContext": "%s"\n}\n' "$session_context"
 fi

Happy to open a PR with this if useful.