{"record":{"id":"b2b6fb84cf699227","repo":"can1357/oh-my-pi","slug":"proxy-returned-malformed-pr-payload","errorCode":null,"errorMessage":"proxy returned malformed pr payload","messagePattern":"proxy returned malformed pr payload","errorType":"exception","errorClass":"GitHubError","httpStatus":500,"severity":"error","filePath":"python/robomp/src/proxy_client.py","lineNumber":722,"sourceCode":"        submitted_at=str(data.get(\"submitted_at\") or \"\"),\n    )\n\n\ndef _pr_file_from(data: Any) -> PullRequestFileInfo:\n    if not isinstance(data, dict):\n        raise GitHubError(500, \"proxy returned malformed pr_file payload\")\n    return PullRequestFileInfo(\n        path=str(data.get(\"path\") or \"\"),\n        status=str(data.get(\"status\") or \"\"),\n        additions=int(data.get(\"additions\") or 0),\n        deletions=int(data.get(\"deletions\") or 0),\n        patch=str(data.get(\"patch\") or \"\"),\n    )\n\n\ndef _pr_from(data: Any) -> PullRequestInfo:\n    if not isinstance(data, dict):\n        raise GitHubError(500, \"proxy returned malformed pr payload\")\n    return PullRequestInfo(\n        repo=str(data[\"repo\"]),\n        number=int(data[\"number\"]),\n        html_url=str(data[\"html_url\"]),\n        head_ref=str(data.get(\"head_ref\") or \"\"),\n        base_ref=str(data.get(\"base_ref\") or \"\"),\n        state=str(data.get(\"state\") or \"open\"),\n        author=str(data.get(\"author\") or \"\"),\n        head_repo=str(data.get(\"head_repo\") or \"\"),\n        title=str(data.get(\"title\") or \"\"),\n        body=str(data.get(\"body\") or \"\"),\n        head_sha=str(data.get(\"head_sha\") or \"\"),\n    )\n\n\n__all__ = [\"GitHubProxyClient\", \"ProxyGitTransport\"]\n","sourceCodeStart":704,"sourceCodeEnd":739,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/proxy_client.py#L704-L739","documentation":"GitHubError(500) raised by _pr_from when the proxy's pull-request payload is not a JSON object. get_pull_request and open_pull_request both require a dict with repo/number/html_url; any scalar or null body means the proxy broke its PR contract.","triggerScenarios":"Calling get_pull_request() for an existing PR when the proxy returns a non-object body, or open_pull_request() when creation succeeds at GitHub but the proxy acknowledges with null/a string instead of the PR object.","commonSituations":"Proxy ACK-only create returning \"created\" instead of the object; proxy error page (HTML/plain text) parsed as a scalar; stale proxy with a different PR envelope; middleware rewriting response bodies.","solutions":["Inspect the raw proxy response for the PR endpoint to see the actual non-dict body.","For open_pull_request: check whether the PR was actually created (list PRs / get by number) before retrying, to avoid duplicates.","Align the proxy version with the client schema; redeploy or upgrade the proxy.","Retry get_pull_request with backoff if the failure is transient.","Report the malformed response to the proxy maintainers with the exact body."],"exampleFix":"# before\npr = client.open_pull_request(\"org/repo\", head, base, title)  # GitHubError 500\n\n# after\ntry:\n    pr = client.open_pull_request(\"org/repo\", head, base, title)\nexcept GitHubError as e:\n    if e.status == 500:\n        # PR may exist; look it up instead of re-opening\n        pr = next((p for p in client.list_issues(\"org/repo\") if p.is_pull_request and p.title == title), None)\n    else:\n        raise","handlingStrategy":"try-catch","validationCode":"body = resp.json()\nif not isinstance(body, dict) or \"html_url\" not in body:\n    raise RuntimeError(f\"proxy pr payload not an object: {body!r}\")","typeGuard":"def is_pr_payload(data: object) -> bool:\n    return isinstance(data, dict) and \"repo\" in data and \"number\" in data and \"html_url\" in data","tryCatchPattern":"try:\n    pr = client.open_pull_request(\"org/repo\", head, base, title)\nexcept GitHubError as e:\n    if e.status == 500 and \"malformed pr payload\" in str(e):\n        pr = lookup_pr_by_head(\"org/repo\", head)  # may exist; avoid duplicate PRs\n    else:\n        raise","preventionTips":["After open_pull_request fails this way, look the PR up by head/base instead of retrying creation — duplicates result otherwise.","Ensure the proxy returns the full PR object on create, not an ACK.","Detect proxy error pages (HTML) before JSON parsing and surface them as 502-style failures.","Pin proxy and client versions; add a get_pull_request smoke check after deploys."],"tags":["proxy","payload-validation","pull-request","github"],"backgroundTag":"proxy-malformed-payload","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}