{"record":{"id":"723c8ce9d126bf1f","repo":"paperclipai/paperclip","slug":"cloud-readiness-job-listing-is-incomplete","errorCode":null,"errorMessage":"Cloud readiness job listing is incomplete.","messagePattern":"Cloud readiness job listing is incomplete\\.","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/cloud-source-verification.mjs","lineNumber":45,"sourceCode":"  }\n  const listing = await api(`/repos/${repository}/actions/workflows/${workflow.id}/runs?head_sha=${sha}&event=push&branch=master&per_page=100`);\n  if (!Array.isArray(listing.workflow_runs) || !Number.isSafeInteger(listing.total_count) ||\n      listing.total_count < 0 || listing.total_count > 100 || listing.workflow_runs.length !== listing.total_count) {\n    throw new Error(\"Cloud readiness run listing is incomplete.\");\n  }\n  const run = listing.workflow_runs.filter((candidate) => trustedRun(candidate, sha, workflow.id))\n    .sort((a, b) => b.id - a.id)[0];\n  if (!run) return undefined;\n\n  // Attempt-specific jobs prevent an earlier successful attempt from blessing\n  // a later rerun. Keep pagination even though today's matrix fits one page.\n  const jobs = [];\n  for (let page = 1; page <= 10; page += 1) {\n    const batch = await api(`/repos/${repository}/actions/runs/${run.id}/attempts/${run.run_attempt}/jobs?per_page=100&page=${page}`);\n    if (!Array.isArray(batch.jobs)) throw new Error(\"Cloud readiness job listing is malformed.\");\n    jobs.push(...batch.jobs);\n    if (batch.jobs.length < 100) break;\n    if (page === 10) throw new Error(\"Cloud readiness job listing is incomplete.\");\n  }\n  const matches = jobs.filter((job) => job.name === sourceVerificationJob);\n  if (matches.length > 1) throw new Error(\"Cloud source verification job is ambiguous.\");\n  const job = matches[0];\n  if (job?.status === \"completed\" && job.conclusion === \"success\" &&\n      job.head_sha === sha && job.run_id === run.id && job.run_attempt === run.run_attempt) {\n    // Re-read the run after its jobs: a rerun that started during polling must\n    // not let the previous attempt through. Changes are retried next poll.\n    const current = await api(`/repos/${repository}/actions/runs/${run.id}`);\n    if (!trustedRun(current, sha, workflow.id) || current.run_attempt !== run.run_attempt) return undefined;\n    return { sha, runId: run.id, attempt: run.run_attempt, jobId: job.id };\n  }\n  if (job?.status === \"completed\" || run.status === \"completed\") {\n    throw new Error(`Cloud source verification did not pass for ${sha} (run ${run.id}, attempt ${run.run_attempt}). Rerun Cloud readiness before retrying the release.`);\n  }\n  return undefined;\n}\n","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/cloud-source-verification.mjs#L27-L63","documentation":"The script pages jobs up to 10 pages of 100. If page 10 comes back with a full 100 jobs, it cannot know the listing ended and throws this error instead of silently truncating. It also fires when a full page arrives on page 10 after the push. Effectively: the run attempt has more than 1000 jobs, which the script refuses to read incompletely.","triggerScenarios":"GET .../runs/{id}/attempts/{n}/jobs?page=10&per_page=100 returns exactly 100 jobs, meaning more pages may exist and the true job list would be truncated.","commonSituations":"A pathological workflow matrix with thousands of jobs on one run attempt; a stubbed api() that keeps returning full pages regardless of page number.","solutions":["Check the run's real job count; a Cloud readiness attempt should never approach 1000 jobs — investigate what generated so many (matrix explosion or repeated reruns).","If it is a stub/test, make later pages return fewer than 100 jobs so pagination terminates.","Raise the page cap (for page <= 10) in scripts/cloud-source-verification.mjs if the workflow legitimately grows beyond 1000 jobs.","Reduce the workflow's job fan-out so the matrix fits within the pagination cap."],"exampleFix":"// before\nfor (let page = 1; page <= 10; page += 1) {\n// after\nfor (let page = 1; page <= 20; page += 1) {","handlingStrategy":"validation","validationCode":"// check job count of the attempt before running the poller\nconst first = await api(`/repos/${repo}/actions/runs/${runId}/attempts/${attempt}/jobs?per_page=100&page=1`);\nif (first.total_count >= 1000) console.warn('attempt exceeds the script pagination cap');","typeGuard":"function fitsPaginationCap(jobCount, cap = 1000) {\n  return Number.isSafeInteger(jobCount) && jobCount < cap;\n}","tryCatchPattern":"try {\n  const proof = await readSourceVerification(sha, api);\n} catch (e) {\n  if (e.message.includes('job listing is incomplete')) {\n    console.error('Attempt has >1000 jobs; shrink the workflow matrix or raise the page cap.');\n  }\n}","preventionTips":["Keep the Cloud readiness matrix small enough to stay far under 1000 jobs per attempt.","In stubbed api() implementations, return fewer than 100 jobs on the last page so pagination terminates.","Audit for accidental matrix duplication before pushing workflow changes."],"tags":["github-actions","pagination","limit-exceeded"],"backgroundTag":"value-out-of-range","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}