langchain-ai/deepagents · error · MarketplaceError

GitHub marketplace URLs must contain exactly owner/repo

Error message

GitHub marketplace URLs must contain exactly owner/repo

What it means

For `https://github.com/...` URLs that are not `.git`-suffixed, the parser splits the path and expects exactly two segments (`owner/repo`) to convert the URL into a GitHub repository source (marketplace.py:180-190). If the path has more than two segments — e.g. a deep link into a subdirectory, a blob/tree URL, or an issues page — the parser refuses rather than guessing, since a marketplace must map to a whole repository.

Source

Thrown at libs/code/deepagents_code/plugins/marketplace.py:190

        except ValueError as exc:
            msg = "Invalid marketplace URL"
            raise MarketplaceError(msg) from exc
        path = parsed.path
        if path.endswith(".git") or "/_git/" in path:
            return RepositoryMarketplaceSource(
                source_type="git", value=url, ref=ref or None
            )
        if parsed.hostname in {"github.com", "www.github.com"}:
            parts = [part for part in path.split("/") if part]
            if len(parts) == _GITHUB_REPO_PART_COUNT:
                repo_path = "/".join(parts)
                git_url = urlunparse(parsed._replace(path=f"/{repo_path}.git"))
                return RepositoryMarketplaceSource(
                    source_type="git", value=git_url, ref=ref or None
                )
            if len(parts) > _GITHUB_REPO_PART_COUNT:
                msg = "GitHub marketplace URLs must contain exactly owner/repo"
                raise MarketplaceError(msg)
        return UrlMarketplaceSource(source_type="url", value=url)

    if value.startswith(("./", "../", "/", "~")):
        return _marketplace_source_from_path(value)

    # Bare relative paths such as `marketplace` (no ./ prefix) are accepted when
    # they exist on disk, before GitHub-shorthand parsing.
    candidate = Path(value).expanduser()
    if candidate.exists():
        return _marketplace_source_from_path(value)

    repo, sep, ref = value.replace("#", "@", 1).partition("@")
    if (
        "/" in value
        and ":" not in value
        and not value.startswith("@")
        and _GITHUB_REPO_RE.match(repo)
    ):

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Use the repository root URL `https://github.com/owner/repo` (exactly two path segments).
  2. Use the GitHub shorthand `owner/repo` instead of a full URL.
  3. If you truly want a file served over HTTPS, host the raw marketplace.json at a non-GitHub URL or use `https://raw.githubusercontent.com/...` only if it ends with `.json` outside the github.com host check (raw.githubusercontent.com is treated as a plain URL source).

Example fix

// before
add_marketplace_source("https://github.com/owner/repo/tree/main/.claude-plugin")
// after
add_marketplace_source("https://github.com/owner/repo")
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def validate_github_repo_url(raw: str) -> str:
    url = raw.partition("#")[0]
    parsed = urlparse(url)
    if parsed.hostname in {"github.com", "www.github.com"}:
        parts = [p for p in parsed.path.split("/") if p]
        if len(parts) != 2:
            raise ValueError(f"Use https://github.com/owner/repo, got: {url}")
    return raw

Type guard

from urllib.parse import urlparse

def is_github_repo_root(value: str) -> bool:
    parsed = urlparse(value.partition("#")[0])
    if parsed.hostname not in {"github.com", "www.github.com"}:
        return False
    return len([p for p in parsed.path.split("/") if p]) == 2

Try / catch

try:
    source = parse_marketplace_source(raw)
except MarketplaceError as exc:
    if "exactly owner/repo" in str(exc):
        # Reduce a deep GitHub web URL to the repo root
        parts = [p for p in urlparse(raw).path.split("/") if p]
        raw = f"https://github.com/{parts[0]}/{parts[1]}"
        source = parse_marketplace_source(raw)
    else:
        raise

Prevention

When it happens

Trigger: `parse_marketplace_source('https://github.com/owner/repo/tree/main/marketplace.json')`, `'https://github.com/owner/repo/blob/main/.claude-plugin/marketplace.json'`, or any GitHub URL with 3+ path segments, via `add_marketplace_source` or `_plugin_repository_source`.

Common situations: Pasting a GitHub web UI URL (tree/blob view) instead of the repo root; linking to a marketplace.json file inside a repo rather than the repo itself; trailing deep paths from GitHub search results.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/dc2c1461036e009a. Report an issue: GitHub.