{"record":{"id":"2a6a4b0c487fbf60","repo":"can1357/oh-my-pi","slug":"proxy-returned-malformed-workflow-run-payload","errorCode":null,"errorMessage":"proxy returned malformed workflow run payload","messagePattern":"proxy returned malformed workflow run payload","errorType":"exception","errorClass":"GitHubError","httpStatus":500,"severity":"error","filePath":"python/robomp/src/proxy_client.py","lineNumber":552,"sourceCode":"        body: dict[str, Any] = {\n            \"repo\": repo,\n            \"workspace_key\": workspace_key,\n            \"branch\": branch,\n            \"tag\": tag,\n            \"expected_head\": expected_head,\n        }\n        if slot_uid is not None:\n            body[\"slot_uid\"] = slot_uid\n        data = self._post(\"/gh/v1/git/push_release\", body)\n        return PushResult(head=str(data.get(\"head\") or expected_head), branch=str(data.get(\"branch\") or branch))\n\n\n# ---------- payload helpers ----------\n\n\ndef _workflow_run_from(data: Any) -> WorkflowRunInfo:\n    if not isinstance(data, dict):\n        raise GitHubError(500, \"proxy returned malformed workflow run payload\")\n    return WorkflowRunInfo(\n        id=int(data.get(\"id\") or 0),\n        name=str(data.get(\"name\") or \"\"),\n        event=str(data.get(\"event\") or \"\"),\n        status=str(data.get(\"status\") or \"\"),\n        conclusion=str(data[\"conclusion\"]) if data.get(\"conclusion\") is not None else None,\n        head_branch=str(data[\"head_branch\"]) if data.get(\"head_branch\") is not None else None,\n        head_sha=str(data.get(\"head_sha\") or \"\"),\n        html_url=str(data.get(\"html_url\") or \"\"),\n        run_attempt=int(data.get(\"run_attempt\") or 1),\n    )\n\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),","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/proxy_client.py#L534-L570","documentation":"`_workflow_run_from` converts a single workflow-run entry returned by the gh-proxy into a `WorkflowRunInfo`. If the proxy response contains an element that is not a JSON object, the client raises `GitHubError(500, ...)` rather than crash with a confusing AttributeError. It signals a broken contract between proxy and client, not a GitHub API problem.","triggerScenarios":"`list_workflow_runs` receiving a proxy payload whose run entries are non-dict values — e.g. the proxy returned an error string/array in the items list, or proxy and client versions disagree on the response schema.","commonSituations":"Proxy deployed at an older/newer version than the client library, a gateway returning HTML/JSON arrays on error, or a custom/forked proxy emitting a different run shape.","solutions":["Check the raw HTTP response body from the proxy for the affected call (log it before parsing)","Upgrade or align proxy server and client library versions so the run schema matches","Verify the proxy (not an intermediary like an auth gateway or CDN) produced the response"],"exampleFix":"# before\nruns = client.list_workflow_runs(repo)\n# after\ndata = client._request(...)  # or add logging\nassert all(isinstance(r, dict) for r in runs_payload), f\"unexpected proxy payload: {type(runs_payload[0])}\"\nruns = [ _workflow_run_from(r) for r in runs_payload ]","handlingStrategy":"type-guard","validationCode":"# validate the raw payload items before mapping\nruns_payload = fetch_raw_runs(repo)\nif not isinstance(runs_payload, list) or not all(isinstance(r, dict) for r in runs_payload):\n    raise ValueError(\"proxy workflow-runs payload malformed\")","typeGuard":"def is_run_payload(v: object) -> TypeGuard[dict]:\n    return isinstance(v, dict) and \"id\" in v","tryCatchPattern":"try:\n    runs = client.list_workflow_runs(repo)\nexcept GitHubError as e:\n    if \"malformed workflow run payload\" in str(e):\n        log.warning(\"proxy/client schema mismatch on workflow runs\", extra={\"repo\": repo})\n        runs = []\n    else:\n        raise","preventionTips":["Keep proxy server and client library versions in lockstep","Contract-test proxy responses against the client's dataclasses/TypedDicts","Log raw response bodies on parse failures to diagnose schema drift","Test against the exact proxy version you deploy, not a newer dev build"],"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"}