Plugin marketplace GCS tarball sync strips execute permissions from .sh files
Summary
The GCS-based marketplace plugin sync mechanism extracts tarballs without preserving execute permissions on .sh files, causing "Permission denied" errors for any plugin that uses shell script hooks.
Reproduction
Environment: macOS (Darwin 25.2.0), Claude Code with multiple profiles (~/.claude/ and ~/.claude-personal/)
Steps:
- Install a plugin with shell script hooks (e.g.,
ralph-loopwhich hashooks/stop-hook.sh) - Wait for the marketplace to sync via the GCS tarball path
- Observe that
.shfiles are written with644permissions (no execute bit) - Any hook execution fails with:
Permission denied
Error message:
Stop hook error: Failed with non-blocking status code: /bin/sh: /Users/<user>/.claude-personal/plugins/marketplaces/claude-plugins-official/plugins/ralph-loop/hooks/stop-hook.sh: Permission denied
Root Cause Analysis
There are two sync paths for marketplace plugins configured with {"source": "github", "repo": "anthropics/claude-plugins-official"}:
- Git clone/pull — preserves file permissions correctly (files get
755) - GCS tarball download — extracts files with
644, stripping the execute bit
Evidence from the same machine, same repo, same known_marketplaces.json config:
| Profile | Sync method | .sh permissions | .git dir present | Last synced |
|---------|-------------|-------------------|--------------------|-------------|
| Work (~/.claude/) | Git clone | 755 (correct) | Yes | Jan 26 |
| Personal (~/.claude-personal/) | GCS tarball | 644 (broken) | No | Mar 26 (multiple times daily) |
The personal profile has a .gcs-sha file instead of a .git directory, and all files share the same timestamp (consistent with bulk tarball extraction). Every .sh file in the personal profile's marketplace directory is 644 — this is not specific to one plugin.
The plugin cache directory (plugins/cache/claude-plugins-official/ralph-loop/unknown/) also has 644 permissions on .sh files, while older SHA-named cache entries have correct 755 permissions (suggesting they were created via the git path).
Impact
- Every plugin with
.shhooks is broken on profiles using the GCS sync path chmod +xfixes are temporary — the next sync cycle (which happens multiple times daily) overwrites with644again- This has been manually fixed 3 times in 2 days before identifying the root cause
- The error fires silently on every stop hook invocation (~57 times across 10 conversations in 2 days)
Expected Behavior
The GCS tarball extraction should either:
- Preserve execute permissions from the tarball (if they're stored correctly)
- Apply
+xto all.shfiles after extraction - Or respect the file permissions that the git-clone path correctly handles
Workaround
None that persists. Converting the marketplace directory to a git clone may work temporarily but risks being overwritten by the GCS sync.
🤖 Generated with Claude Code
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗