{"record":{"id":"eec53ac7cd252742","repo":"can1357/oh-my-pi","slug":"proxy-returned-malformed-release-payload","errorCode":null,"errorMessage":"proxy returned malformed release payload","messagePattern":"proxy returned malformed release payload","errorType":"exception","errorClass":"GitHubError","httpStatus":500,"severity":"error","filePath":"python/robomp/src/proxy_client.py","lineNumber":582,"sourceCode":"\n\ndef _workflow_job_from(data: Any) -> WorkflowJobInfo:\n    if not isinstance(data, dict):\n        raise GitHubError(500, \"proxy returned malformed workflow job payload\")\n    return WorkflowJobInfo(\n        id=int(data.get(\"id\") or 0),\n        run_id=int(data.get(\"run_id\") or 0),\n        name=str(data.get(\"name\") or \"\"),\n        status=str(data.get(\"status\") or \"\"),\n        conclusion=str(data[\"conclusion\"]) if data.get(\"conclusion\") is not None else None,\n        html_url=str(data.get(\"html_url\") or \"\"),\n        failed_steps=tuple(str(step) for step in data.get(\"failed_steps\") or []),\n    )\n\n\ndef _release_from(data: Any) -> ReleaseInfo:\n    if not isinstance(data, dict):\n        raise GitHubError(500, \"proxy returned malformed release payload\")\n    name = data.get(\"name\")\n    return ReleaseInfo(\n        tag=str(data.get(\"tag\") or \"\"),\n        name=str(name) if name is not None else None,\n        draft=bool(data.get(\"draft\")),\n        prerelease=bool(data.get(\"prerelease\")),\n        html_url=str(data.get(\"html_url\") or \"\"),\n        asset_names=tuple(str(asset) for asset in data.get(\"asset_names\") or []),\n    )\n\n\ndef _repo_from(data: Any) -> RepoInfo:\n    if not isinstance(data, dict):\n        raise GitHubError(500, \"proxy returned malformed repo payload\")\n    return RepoInfo(\n        full_name=str(data[\"full_name\"]),\n        default_branch=str(data[\"default_branch\"]),\n        clone_url=str(data[\"clone_url\"]),","sourceCodeStart":564,"sourceCodeEnd":600,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/proxy_client.py#L564-L600","documentation":"`_release_from` converts a release object from the gh-proxy into `ReleaseInfo`. If the value returned for a release is not a JSON object, the client raises `GitHubError(500, ...)` with this message instead of failing later on field access. It indicates the proxy violated the expected release payload shape.","triggerScenarios":"`get_release_by_tag` receiving a non-dict for the release — e.g. the proxy returned `null`/a string when the release lookup result shape differs, or an intermediary error body was parsed as the release.","commonSituations":"Proxy/client version skew after a release-schema change, custom proxy returning plain tag strings, gateway error pages, or a forked proxy with a different contract.","solutions":["Capture and inspect the raw response body from the proxy for get_release_by_tag","Bring proxy and client library versions into agreement on the release schema","Confirm the request reaches the real gh-proxy service, not an error page from an intermediary"],"exampleFix":"# before\nrelease = client.get_release_by_tag(repo, tag)\n# after\ntry:\n    release = client.get_release_by_tag(repo, tag)\nexcept GitHubError as e:\n    if \"malformed release payload\" in str(e):\n        release = None  # fall back to manual tag handling\n    else:\n        raise","handlingStrategy":"type-guard","validationCode":"release_payload = fetch_raw_release(repo, tag)\nif not isinstance(release_payload, dict):\n    raise ValueError(f\"proxy release payload is {type(release_payload).__name__}, expected object\")","typeGuard":"def is_release_payload(v: object) -> TypeGuard[dict]:\n    return isinstance(v, dict) and \"tag\" in v","tryCatchPattern":"try:\n    release = client.get_release_by_tag(repo, tag)\nexcept GitHubError as e:\n    if \"malformed release payload\" in str(e):\n        log.warning(\"release schema mismatch\", extra={\"repo\": repo, \"tag\": tag})\n        release = None\n    else:\n        raise","preventionTips":["Keep client and proxy schemas synchronized (shared types or contract tests)","Inspect raw bodies when the proxy is behind gateways/CDNs that can alter them","Version the proxy API and have the client check compatibility at startup","Fix proxies to return null/absent for missing releases rather than alternate shapes"],"tags":["proxy","contract-mismatch","parsing","client-error"],"backgroundTag":"proxy-payload-schema-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}