{"record":{"id":"2490586d9f1639df","repo":"paperclipai/paperclip","slug":"invalid-migrator-producer-response","errorCode":null,"errorMessage":"Invalid migrator producer response.","messagePattern":"Invalid migrator producer response\\.","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/cloud-readiness.mjs","lineNumber":24,"sourceCode":"import path from \"node:path\";\nimport { imageExists, versionFor } from \"./preview-artifacts.mjs\";\nimport { verifyPublished } from \"./cloud-migrator-artifacts.mjs\";\n\nconst repository = \"paperclipai/paperclip\";\nconst workflow = \".github/workflows/cloud-migrator-artifacts.yml\";\n\nexport async function migratorPublished(sha, fetchImpl, token) {\n  let pending = false;\n  const failures = [];\n  for (let page = 1; page <= 10; page++) {\n    const response = await fetchImpl(`https://api.github.com/repos/${repository}/actions/workflows/cloud-migrator-artifacts.yml/runs?branch=master&head_sha=${sha}&per_page=100&page=${page}`, {\n      headers: { Accept: \"application/vnd.github+json\", ...(token ? { Authorization: `Bearer ${token}` } : {}) },\n      redirect: \"error\", signal: AbortSignal.timeout(30_000),\n    });\n    if (!response.ok) throw new Error(`Migrator producer lookup failed: HTTP ${response.status}`);\n    const body = await response.json();\n    if (!Array.isArray(body.workflow_runs) || !Number.isSafeInteger(body.total_count) || body.total_count < 0 ||\n        (page === 1 && (body.total_count === 0) !== (body.workflow_runs.length === 0))) throw new Error(\"Invalid migrator producer response.\");\n    if (body.total_count === 0) return false;\n    for (const run of body.workflow_runs) {\n      if (run.head_sha !== sha || run.head_branch !== \"master\" || run.path !== workflow ||\n          run.head_repository?.id !== 1170821064 || run.head_repository.full_name !== repository ||\n          ![\"push\", \"workflow_dispatch\"].includes(run.event)) throw new Error(\"Migrator producer identity mismatch.\");\n      // Publication is immutable. A later failed manual run must not hide a\n      // successful exact-source publisher; the signed bundle is checked next.\n      if (run.status === \"completed\" && run.conclusion === \"success\") return true;\n      if (run.status !== \"completed\") pending = true;\n      else failures.push(`${run.id}: ${run.conclusion}`);\n    }\n    if (page * 100 >= body.total_count) {\n      if (pending) return false;\n      throw new Error(`Migrator producers failed: ${failures.join(\", \")}.`);\n    }\n  }\n  throw new Error(\"Too many migrator producer runs to establish publication.\");\n}","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/cloud-readiness.mjs#L6-L42","documentation":"After a successful HTTP response, migratorPublished() validates the GitHub workflow-runs payload shape: workflow_runs must be an array, total_count a safe non-negative integer, and on page 1 zero total_count must correspond to zero listed runs. Anything else throws this error. It guards against unexpected API response shapes.","triggerScenarios":"GitHub returns 200 with a malformed/JSON body missing workflow_runs or total_count (e.g. an HTML error page behind a captive proxy, a changed API contract, or total_count===0 with runs listed or vice versa).","commonSituations":"Corporate proxies or API gateways rewriting responses; GitHub API preview/contract changes; intermediary auth portals returning 200 HTML; mocking layers returning incomplete fixtures.","solutions":["Print the raw response body to see what actually came back (proxy HTML vs JSON).","Bypass proxies/VPNs that inject 200 HTML responses and retry.","Pin an explicit X-GitHub-Api-Version header and check for GitHub API changelog updates affecting the actions/workflows/runs endpoint.","If using a test double for fetchImpl, make it return { total_count, workflow_runs } consistent with each other."],"exampleFix":"// before\nconst body = await response.json(); // assumed valid\n// after\nconst body = await response.json();\nif (!Array.isArray(body.workflow_runs)) console.error(\"Unexpected body:\", body);","handlingStrategy":"type-guard","validationCode":"const res = await fetch(url, opts);\nconst text = await res.text();\nlet body; try { body = JSON.parse(text); } catch { throw new Error(\"Non-JSON body (proxy?): \" + text.slice(0, 120)); }","typeGuard":"const looksLikeRunList = (b) => b && Array.isArray(b.workflow_runs) && Number.isSafeInteger(b.total_count) && b.total_count >= 0;","tryCatchPattern":"try {\n  await waitForCloudArtifacts(sha);\n} catch (error) {\n  if (error.message === \"Invalid migrator producer response.\") {\n    console.error(\"GitHub returned an unexpected payload — check proxies/GitHub status.\");\n  } else throw error;\n}","preventionTips":["Bypass corporate proxies that rewrite 200 bodies","Check githubstatus.com when this appears suddenly","Keep mocked fetch fixtures shape-accurate in tests"],"tags":["github-api","unexpected-response-shape"],"backgroundTag":"unexpected-response-shape","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}