Plugin marketplace: .sh files downloaded from GCS without executable permissions

Resolved 💬 2 comments Opened Mar 26, 2026 by simon-thebunch Closed Mar 27, 2026

Bug Description

When the plugin marketplace syncs files from GCS (e.g. on Claude Code update), all .sh files are downloaded without executable permissions (-rw-r--r-- instead of -rwxr-xr-x). This causes hook scripts to fail with:

Stop hook error: Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: Permission denied

Root Cause

GCS object storage does not preserve Unix file permissions as object metadata. When files are downloaded, they receive the default umask permissions. The plugin sync code does not apply chmod +x to .sh files after download.

Reproduction Steps

  1. Install/update Claude Code (triggers plugin re-sync)
  2. Any plugin with hook .sh files will have them downloaded as non-executable
  3. On next session, hooks fail with Permission denied

Expected Behavior

.sh files downloaded from the plugin marketplace should automatically have +x applied.

Actual Behavior

All .sh files in ~/.claude/plugins/marketplaces/claude-plugins-official/ are downloaded as -rw-r--r--.

Affected files (example from one install):

  • plugins/ralph-loop/hooks/stop-hook.sh
  • plugins/ralph-loop/scripts/setup-ralph-loop.sh
  • plugins/learning-output-style/hooks-handlers/session-start.sh
  • plugins/explanatory-output-style/hooks-handlers/session-start.sh
  • (and others)

Workaround

find ~/.claude/plugins -name '*.sh' ! -perm -u+x -exec chmod +x {} +

Or add a SessionStart hook in settings.json to re-apply permissions on every session.

Environment

  • Claude Code: 2.1.84
  • macOS (Apple Silicon / Darwin 25.3.0)
  • Pattern: occurs on every Claude Code update that triggers a plugin re-sync

View original on GitHub ↗

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