Skip to content

fix(plugin): refuse an unset CLAUDE_PLUGIN_ROOT instead of guessing $PWD - #430

Merged
cdeust merged 1 commit into
mainfrom
fix/plugin-root-fallback
Aug 10, 2026
Merged

fix(plugin): refuse an unset CLAUDE_PLUGIN_ROOT instead of guessing $PWD#430
cdeust merged 1 commit into
mainfrom
fix/plugin-root-fallback

Conversation

@cdeust

@cdeust cdeust commented Aug 10, 2026

Copy link
Copy Markdown
Owner

All nine hook commands in .claude-plugin/plugin.json resolved their root as ${CLAUDE_PLUGIN_ROOT:-$PWD}. That fallback cannot ever be correct: scripts/launcher.py lives inside the plugin, never in the user's working directory. When the variable is unset the hook runs python3 against a path that is wrong by construction and reports

can't open file '<cwd>/scripts/launcher.py': [Errno 2] No such file or directory

— an error naming the wrong cause, which sends the reader looking at their own project instead of at the plugin.

How it surfaced. On 2026-08-10, every Cortex hook failed this way from an unrelated repository, repeatedly, before the cause was traced back here. The message pointed at a path that had never been right, so it read as a local problem rather than a plugin one.

A fallback that guarantees a wrong path is not a fallback. It converts a clear configuration error — "this variable is not set" — into a misleading runtime one, and it belongs to the same family as a gate that opens when it cannot decide.

The change

The hooks now refuse and name the missing variable:

if [ -z "${CLAUDE_PLUGIN_ROOT:-}" ]; then
  echo "cortex hook: CLAUDE_PLUGIN_ROOT is unset; refusing to guess the plugin root" >&2
  exit 1
fi
ROOT="$CLAUDE_PLUGIN_ROOT"

Nine occurrences, no other edit. The file was re-parsed as JSON after the change.

Verified both directions on the final state

Unset — refuses, names the variable:

$ eval "$SESSION_END_HOOK_COMMAND" </dev/null
cortex hook: CLAUDE_PLUGIN_ROOT is unset; refusing to guess the plugin root
exit=1

Set — behaviour identical to before the change; the hook reaches the launcher:

$ export CLAUDE_PLUGIN_ROOT="$PWD"; eval "$SESSION_END_HOOK_COMMAND" </dev/null
Original error was: 'numpy._core._multiarray_umath'
exit=1

That trailing numpy failure is pre-existing and unrelated — the same command from origin/main produces it identically in this virtualenv. It is the proof the guard does not change the normal path, not a regression introduced here.

exit 1 on these events is a non-blocking error by the hooks contract, so a misconfigured install now reports the real cause without stopping the session.

All nine hook commands resolved their root as ${CLAUDE_PLUGIN_ROOT:-$PWD}.
That fallback cannot work: the launcher lives inside the plugin, never in
the user's working directory, so when the variable is unset the hook runs
python3 against a path that is wrong by construction and reports
"can't open file '<cwd>/scripts/launcher.py'" — an error that names the
wrong cause and sends the reader looking at their own project.

Measured 2026-08-10 from an unrelated repository: every Cortex hook failed
that way, silently, until the cause was traced back here. A fallback that
guarantees a wrong path is not a fallback; it converts a clear
configuration error into a misleading runtime one.

The hooks now refuse and name the missing variable. Verified both ways on
the final state: unset -> "CLAUDE_PLUGIN_ROOT is unset; refusing to guess
the plugin root", exit 1; set -> identical behaviour to before the change
(it reaches the launcher). JSON re-parsed after the edit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01263uv1QqR8TVzw2jXYUrXn
@cdeust
cdeust merged commit ebae490 into main Aug 10, 2026
25 checks passed
@cdeust
cdeust deleted the fix/plugin-root-fallback branch August 10, 2026 22:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant