Skip to content

Fix/claude code hook python3 fallback - #1

Merged
jessburnett merged 2 commits into
mainfrom
fix/claude-code-hook-python3-fallback
Jul 29, 2026
Merged

Fix/claude code hook python3 fallback#1
jessburnett merged 2 commits into
mainfrom
fix/claude-code-hook-python3-fallback

Conversation

@jessburnett

Copy link
Copy Markdown
Owner

Title

fix: fall back python3 -> python -> graceful skip in SessionStart hook

Summary

Add a small POSIX wrapper and point the SessionStart hook at it so the hook first tries python3, falls back to python, and otherwise exits 0 with a benign skip message. This prevents a SyntaxError crash on systems where bare python resolves to Python 2.

Problem

  • The Hook: claude-code/hooks/hooks.json invoked capture.py directly with a bare "python":
    "command": "python \"${CLAUDE_PLUGIN_ROOT}/engine/capture.py\" hook"
  • On machines where the unqualified python resolves to Python 2, capture.py (which uses PEP 3107 annotations) raises a SyntaxError (capture.py:54) and crashes the SessionStart hook.
  • This crash was reproduced locally: running the original command under a python→python2 alias produced the SyntaxError at line 54, matching the user report.

Root cause

Fix / Changes

  • Added new executable wrapper script: claude-code/hooks/session-start.sh
    • Behavior:
      1. If python3 is available, exec python3 "${CLAUDE_PLUGIN_ROOT}/engine/capture.py" hook
      2. Else if python is available, exec python "${CLAUDE_PLUGIN_ROOT}/engine/capture.py" hook
      3. Else print JSON: {"continue":true,"systemMessage":"AgenTrust: skipped the integrity check because Python is unavailable."} and exit 0
  • Updated claude-code/hooks/hooks.json to invoke the wrapper:
    -            "command": "python \"${CLAUDE_PLUGIN_ROOT}/engine/capture.py\" hook",
    +            "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh\"",
  • Files changed (local branch fix/claude-code-hook-python3-fallback):
    • M claude-code/hooks/hooks.json
    • A claude-code/hooks/session-start.sh (executable)

Testing

  • Local manual checks run in the repository:
    • With CLAUDE_PLUGIN_ROOT set, wrapper executed via python3 and returned valid SessionStart JSON and exit 0:
      CLAUDE_PLUGIN_ROOT="$(pwd)/claude-code" sh claude-code/hooks/session-start.sh
      → {"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"AgenTrust: agent composition unchanged ..."}}
      
    • Hiding both python3 and python on PATH (test harness) produced the graceful skip JSON and exit 0.
    • Attempt to run wrapper without CLAUDE_PLUGIN_ROOT set produced the expected file-not-found exit but demonstrates path usage; with CLAUDE_PLUGIN_ROOT set it exercises the real execution path.
  • CI: no engine logic changed; existing CI runs on Python 3 (matrix 3.9/3.11/3.13 etc). This change is a compatibility/infra fix and should not affect tests; nonetheless run the claude-code tests in CI as usual.

How to reproduce locally

  • Reproduce the crash (before change):
    • Ensure your python resolves to Python 2 (or use a PATH that points python to a py2 binary)
    • Run: CLAUDE_PLUGIN_ROOT="$(pwd)/claude-code" sh -c 'python "${CLAUDE_PLUGIN_ROOT}/engine/capture.py" hook'
    • Expect: SyntaxError at capture.py:54
  • After applying this patch locally:
    • CLAUDE_PLUGIN_ROOT="$(pwd)/claude-code" sh claude-code/hooks/session-start.sh
    • Expect: valid JSON output and exit 0 (with python3) or graceful skip JSON if no python available

Rollout / Migration

  • To update local installs (maintainers / users who use the cached plugin):
    • Copy the updated hooks into the local plugin cache (example path on your machine):
      cp claude-code/hooks/hooks.json ~/.claude/plugins/cache/agentrust-io/agentrust-claude-code/0.2.0/hooks/hooks.json
      cp claude-code/hooks/session-start.sh ~/.claude/plugins/cache/agentrust-io/agentrust-claude-code/0.2.0/hooks/session-start.sh
      chmod +x ~/.claude/plugins/cache/agentrust-io/agentrust-claude-code/0.2.0/hooks/session-start.sh
      
    • Or, reinstall the plugin from the updated marketplace clone so the cache is rebuilt.
  • No user-facing configuration changes needed beyond the cache update or reinstall.

Backwards compatibility & Risks

  • Backwards-compatible: systems with python3 continue to run python3.
  • Systems with only python (no python3) will continue to run python (fallback).
  • Systems with neither will receive a benign skip message and the session will continue; this is intentional to avoid crashing the host CLAUDE session.
  • Risk: If someone expects the integrity check to always run, systems without any Python interpreter will skip the check — documented in rollout/migration.

Security & Privacy

  • No network calls introduced. The wrapper only chooses which local Python to exec.
  • No sensitive data is logged or transmitted by this change.

Related issues / PRs

Reviewer checklist

Co-authors / Acknowledgements

  • Discovered and triaged by Jessica Burnett (local repo and tests) with Claude.
  • Mirror pattern implemented to match plugins/agentrust-codex/scripts/session-start.sh.

Bare `python` resolves to Python 2 on machines where it's aliased that
way. capture.py uses PEP 3107 annotations, which is a SyntaxError under
py2, crashing the hook before AgenTrust ever runs.

Mirrors the fallback pattern already shipped in plugins/agentrust-codex/
scripts/session-start.sh: try python3, fall back to python, and if
neither is on PATH, emit a benign skip message and exit 0 instead of
crashing the session.
Layout diagram and quickstart still described the hook as calling
engine/capture.py directly. Update both to match the python3/python
fallback wrapper added in the previous commit.
@jessburnett
jessburnett merged commit 5a93757 into main Jul 29, 2026
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