Plugin system does not preserve execute permission on hook scripts
Resolved 💬 5 comments Opened Mar 27, 2026 by felipegdearaujo Closed Apr 18, 2026
Description
When the plugin system syncs files from the cache directory to ~/.claude/plugins/marketplaces/, it does not preserve the execute bit (+x) on shell scripts. This causes hook scripts to fail with "Permission denied" errors.
Steps to Reproduce
- Have the
ralph-loopplugin installed (from the official marketplace) - Start a Claude Code session — the stop hook fails intermittently:
Stop hook error: Failed with non-blocking status code: /bin/sh:
/Users/<user>/.claude/plugins/marketplaces/claude-plugins-official/plugins/ralph-loop/hooks/stop-hook.sh: Permission denied
- Running
chmod +xon the script fixes it temporarily, but the next plugin sync resets permissions back to644.
Root Cause
The cached copies of the script have mixed permissions:
# Cache (hashed versions) — correct:
-rwxr-xr-x stop-hook.sh (78497c524da3)
-rwxr-xr-x stop-hook.sh (61c0597779bd)
# Cache ("unknown" version) — missing +x:
-rw-r--r-- stop-hook.sh (unknown)
# Marketplaces (active copy) — missing +x:
-rw-r--r-- stop-hook.sh
The plugin system copies files without preserving the execute bit, so the active copy under marketplaces/ ends up as 644 even though the source had 755.
Suggested Fix
Two complementary fixes:
- Plugin system: Preserve file permissions when copying from cache to
marketplaces/(e.g., usecp -por explicitlychmod +x *.shafter copy). - Hook execution: For
hooks.jsoncommands referencing.shfiles, consider invoking them viabash <script>instead of relying on the execute bit, making execution independent of file permissions.
Workaround
Add to ~/.zshrc:
chmod +x ~/.claude/plugins/marketplaces/claude-plugins-official/plugins/ralph-loop/hooks/*.sh 2>/dev/null
Environment
- macOS (Darwin 25.2.0)
- Claude Code with official marketplace plugins
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗