OpenHands / OpenHands/extensions
Marketplace catalog unreachable via symlinked .claude-plugin path (breaks plugin-directory / `github://openhands/extensions`)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 148
- Forks
- 90
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 36
Description
Summary
When the plugin-directory is configured with MARKETPLACE_SOURCE=github://openhands/extensions, the marketplace catalog does not render — the Plugin Directory shows an empty catalog.
Root cause: the catalog is exposed through a double symlink, and raw.githubusercontent.com does not dereference symlinks through directory paths.
How plugin-directory fetches the catalog
openhands/plugin-directory builds a hardcoded raw URL from the source config (server/app/services/marketplace_service.py):
CATALOG_PATH = ".claude-plugin/marketplace.json"
url = f"https://raw.githubusercontent.com/{owner}/{repo}/{ref}/{catalog_path}"
So it requests exactly:
https://raw.githubusercontent.com/openhands/extensions/main/.claude-plugin/marketplace.json
The symlink chain in this repo
Traced via the GitHub git Trees API (mode=120000 = symlink):
| Path | Git mode | Type | Target |
|---|---|---|---|
.claude-plugin |
120000 |
symlink | .plugin |
.plugin/marketplace.json |
120000 |
symlink | ../marketplaces/openhands-extensions.json |
marketplaces/openhands-extensions.json |
100644 |
real file | (the actual catalog) |
Verification
raw.githubusercontent.com serves the raw blob content stored in git. A symlink is stored as a blob whose content is the link-target text, and the raw server does not traverse symlinked directory paths:
.claude-plugin/marketplace.json→ HTTP 404 (symlinked directory not traversable).plugin/marketplace.json→ returns the literal string../marketplaces/openhands-extensions.json(the symlink blob body), not the JSON catalog — so even if level 1 resolved, JSON parsing would failmarketplaces/openhands-extensions.json→ HTTP 200, the real catalog JSON
As a result, plugin-directory's fetch_catalog() raises CatalogUnavailableError on the 404, the plugins list comes back empty, and nothing renders. This is a server-side (GitHub raw) limitation, not something the plugin-directory client can work around — the catalog simply never arrives.
Suggested fix
- In this repo (simplest): commit a real
.claude-plugin/marketplace.jsonfile (or a real.claude-plugin/directory) instead of the symlink chain, so the catalog is reachable at the path plugin-directory (and other consumers using the standard.claude-plugin/marketplace.jsonconvention) expects.
Happy to open a PR for either direction if helpful.
This issue was created by an AI agent (OpenHands) on behalf of a user.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Inspect marketplaces/openhands-extensions.json and the current .claude-plugin symlink path, then verify the raw GitHub URL used by plugin-directory. Make the catalog available as a real file at .claude-plugin/marketplace.json and confirm that the raw URL returns the catalog JSON instead of a 404 or symlink target.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- github, python
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100