can1357/oh-my-pi · error · GitHubError

proxy returned malformed workflow job payload

Error message

proxy returned malformed workflow job payload

What it means

`_workflow_job_from` maps one workflow-job entry from the gh-proxy response into `WorkflowJobInfo`. If an entry is not a JSON object, the client raises `GitHubError(500, ...)` because it cannot safely read the expected fields. This is a client-side contract-violation error against the proxy's response format.

Source

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

def _workflow_run_from(data: Any) -> WorkflowRunInfo:
    if not isinstance(data, dict):
        raise GitHubError(500, "proxy returned malformed workflow run payload")
    return WorkflowRunInfo(
        id=int(data.get("id") or 0),
        name=str(data.get("name") or ""),
        event=str(data.get("event") or ""),
        status=str(data.get("status") or ""),
        conclusion=str(data["conclusion"]) if data.get("conclusion") is not None else None,
        head_branch=str(data["head_branch"]) if data.get("head_branch") is not None else None,
        head_sha=str(data.get("head_sha") or ""),
        html_url=str(data.get("html_url") or ""),
        run_attempt=int(data.get("run_attempt") or 1),
    )


def _workflow_job_from(data: Any) -> WorkflowJobInfo:
    if not isinstance(data, dict):
        raise GitHubError(500, "proxy returned malformed workflow job payload")
    return WorkflowJobInfo(
        id=int(data.get("id") or 0),
        run_id=int(data.get("run_id") or 0),
        name=str(data.get("name") or ""),
        status=str(data.get("status") or ""),
        conclusion=str(data["conclusion"]) if data.get("conclusion") is not None else None,
        html_url=str(data.get("html_url") or ""),
        failed_steps=tuple(str(step) for step in data.get("failed_steps") or []),
    )


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,

View on GitHub (pinned to 9690622007)

Solutions

  1. Log/inspect the raw proxy response to see what the job entries actually look like
  2. Align client and proxy versions (upgrade the robomp client or the proxy)
  3. Bypass intermediaries (gateways, caches) that may replace the response body
  4. If you control the proxy, ensure job list items are always JSON objects

Example fix

// before
jobs = client.list_workflow_jobs(run_id)  # GitHubError on non-dict entry
// after
try:
    jobs = client.list_workflow_jobs(run_id)
except GitHubError as e:
    log.warning("proxy job payload malformed", extra={"run_id": run_id, "err": str(e)})
    jobs = []
Defensive patterns

Strategy: type-guard

Validate before calling

jobs_payload = fetch_raw_jobs(run_id)
if not isinstance(jobs_payload, list) or not all(isinstance(j, dict) for j in jobs_payload):
    raise ValueError("proxy workflow-jobs payload malformed")

Type guard

def is_job_payload(v: object) -> TypeGuard[dict]:
    return isinstance(v, dict) and "id" in v and "run_id" in v

Try / catch

try:
    jobs = client.list_workflow_jobs(run_id)
except GitHubError as e:
    if "malformed workflow job payload" in str(e):
        log.warning("proxy job schema mismatch", extra={"run_id": run_id})
        jobs = []
    else:
        raise

Prevention

When it happens

Trigger: `list_workflow_jobs` receiving a job list whose elements are strings/numbers/null instead of objects — typically a proxy version skew, an error page parsed as data, or a corrupted/partial response.

Common situations: Proxy upgraded to a new job schema while the client library is pinned old (or vice versa), reverse proxy or service mesh injecting error bodies, custom proxy implementations returning different shapes.

Understand the failure class

Related errors


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