can1357/oh-my-pi · error · GitHubError

proxy returned malformed repo payload

Error message

proxy returned malformed repo payload

What it means

`_repo_from` builds a `RepoInfo` from the gh-proxy's repo payload and requires it to be a JSON object; otherwise it raises `GitHubError(500, ...)`. The subsequent code also accesses `full_name`, `default_branch`, and `clone_url` unconditionally, so the object check is the guard that produces this clear error instead of a KeyError downstream.

Source

Thrown at python/robomp/src/proxy_client.py:596


def _release_from(data: Any) -> ReleaseInfo:
    if not isinstance(data, dict):
        raise GitHubError(500, "proxy returned malformed release payload")
    name = data.get("name")
    return ReleaseInfo(
        tag=str(data.get("tag") or ""),
        name=str(name) if name is not None else None,
        draft=bool(data.get("draft")),
        prerelease=bool(data.get("prerelease")),
        html_url=str(data.get("html_url") or ""),
        asset_names=tuple(str(asset) for asset in data.get("asset_names") or []),
    )


def _repo_from(data: Any) -> RepoInfo:
    if not isinstance(data, dict):
        raise GitHubError(500, "proxy returned malformed repo payload")
    return RepoInfo(
        full_name=str(data["full_name"]),
        default_branch=str(data["default_branch"]),
        clone_url=str(data["clone_url"]),
        private=bool(data.get("private", False)),
    )


def _issue_from(data: Any) -> IssueInfo:
    if not isinstance(data, dict):
        raise GitHubError(500, "proxy returned malformed issue payload")
    labels = data.get("labels") or []
    return IssueInfo(
        repo=str(data["repo"]),
        number=int(data["number"]),
        title=str(data.get("title") or ""),
        body=str(data.get("body") or ""),
        state=str(data.get("state") or "open"),

View on GitHub (pinned to 9690622007)

Solutions

  1. Log the raw proxy response for get_repo and compare with the expected RepoInfo fields
  2. Upgrade/align the proxy server and robomp client versions
  3. Check for intermediaries replacing the response body and test the proxy endpoint directly
  4. If you control the proxy, ensure the repo response always includes full_name, default_branch, and clone_url

Example fix

// before
info = client.get_repo("owner/name")
// after
try:
    info = client.get_repo("owner/name")
except GitHubError as e:
    log.error("repo payload malformed", extra={"repo": "owner/name", "detail": str(e)})
    raise
Defensive patterns

Strategy: type-guard

Validate before calling

repo_payload = fetch_raw_repo(name)
if not isinstance(repo_payload, dict):
    raise ValueError(f"proxy repo payload is {type(repo_payload).__name__}, expected object")
missing = {"full_name", "default_branch", "clone_url"} - repo_payload.keys()
if missing:
    raise ValueError(f"proxy repo payload missing fields: {sorted(missing)}")

Type guard

def is_repo_payload(v: object) -> TypeGuard[dict]:
    return isinstance(v, dict) and {"full_name", "default_branch", "clone_url"} <= v.keys()

Try / catch

try:
    info = client.get_repo(name)
except GitHubError as e:
    if "malformed repo payload" in str(e):
        log.error("proxy repo schema mismatch", extra={"repo": name})
        raise ConnectionError(f"gh-proxy at {proxy_url} returned incompatible repo payload") from e
    raise

Prevention

When it happens

Trigger: `get_repo` receiving a non-dict repo payload from the proxy — version skew between proxy and client, an error string/array/body returned instead of the repo object, or a custom proxy emitting a different repo shape.

Common situations: Proxy misdeployment or old proxy binary behind a new client, authentication gateways returning JSON error objects wrapped differently, network appliances substituting bodies, forked proxies.

Understand the failure class

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/7037d01d109bbac4. Report an issue: GitHub.