paperclipai/paperclip · error · Error
Cloud readiness run listing is incomplete.
Error message
Cloud readiness run listing is incomplete.
What it means
readSourceVerification validates the GitHub Actions run listing for the cloud-readiness workflow before trusting it. This error means the response's workflow_runs array or total_count field failed the completeness invariant: missing/non-array workflow_runs, missing/non-integer/negative/over-100 total_count, or array length not equal to total_count. The library throws rather than blessing a release off a truncated or unexpected listing.
Solutions
- Confirm the workflow actually has runs for that sha on master with event=push; a wrong/incomplete sha yields no listing fields.
- If more than 100 runs match, delete old workflow runs or adjust the query so the result fits one page (total_count <= 100 is required).
- Inspect the raw JSON at https://api.github.com/repos/paperclipai/paperclip/actions/workflows/{id}/runs?head_sha=<sha>&event=push&branch=master to see the actual envelope.
- If api() is injected (tests/scripts), fix the stub to return { total_count, workflow_runs: [...] } with matching lengths.
- Rerun the release poll once GitHub API behavior is normal; the script retries transient transport errors but treats a bad listing as fatal.
Example fix
// before: stubbed api returning only runs
const api = async () => ({ workflow_runs: [run] });
// after
const api = async () => ({ total_count: 1, workflow_runs: [run] }); Defensive patterns
Strategy: validation
Validate before calling
const listing = await api(path);
if (!listing || !Array.isArray(listing.workflow_runs) || !Number.isSafeInteger(listing.total_count) || listing.total_count < 0 || listing.total_count > 100 || listing.workflow_runs.length !== listing.total_count) throw new Error('run listing incomplete'); Type guard
function isValidListing(l) {
return !!l && Array.isArray(l.workflow_runs) && Number.isSafeInteger(l.total_count) &&
l.total_count >= 0 && l.total_count <= 100 && l.workflow_runs.length === l.total_count;
} Try / catch
try {
const proof = await readSourceVerification(sha, api);
} catch (e) {
if (e.message.includes('run listing is incomplete')) {
console.error('GitHub returned an unexpected runs envelope; check sha/branch filters and retry later.');
}
} Prevention
- Keep the number of push runs per sha on master below 100 (the script requires total_count <= 100).
- When stubbing api() in tests, always include total_count consistent with workflow_runs.length.
- Pin checks to full 40-char shas so head_sha filters return exactly the intended runs.
- Log the raw listing JSON when this error fires to spot API envelope changes early.
When it happens
Trigger: Calling readSourceVerification (directly or via waitForSourceVerification) when the GET /repos/paperclipai/paperclip/actions/workflows/{id}/runs?head_sha=...&event=push&branch=master&per_page=100 response lacks workflow_runs, has a malformed or out-of-range total_count, or returns fewer runs than total_count (pagination beyond one page of 100).
Common situations: A mock or stubbed api() returns a partial fixture; GitHub changes the response envelope shape; more than 100 runs match head_sha=event=push&branch=master (listing genuinely overflows per_page=100); an API gateway returns HTML/empty JSON that decoded into an unexpected object.
Related errors
- Cloud readiness job listing is malformed.
- Cloud readiness job listing is incomplete.
- Cloud artifacts timed out for
- Cloud source verification did not pass for
- Cloud source verification job is ambiguous.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/0588246ad971d168.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloud-source-verification.mjs:31
return run.workflow_id === workflowId && run.path === workflowPath &&
run.repository?.full_name === repository && run.head_repository?.full_name === repository &&
run.head_sha === sha && run.head_branch === "master" && run.event === "push" &&
Number.isSafeInteger(run.id) && run.id > 0 &&
Number.isSafeInteger(run.run_attempt) && run.run_attempt > 0;
}
// Consume one versioned job, independent of image/migrator availability. A
// failed image build must not invalidate source checks that already passed.
export async function readSourceVerification(sha, api) {
assertSha(sha);
const workflow = await api(`/repos/${repository}/actions/workflows/cloud-readiness.yml`);
if (workflow.path !== workflowPath || !Number.isSafeInteger(workflow.id) || workflow.id < 1) {
throw new Error("Cloud readiness workflow identity does not match.");
}
const listing = await api(`/repos/${repository}/actions/workflows/${workflow.id}/runs?head_sha=${sha}&event=push&branch=master&per_page=100`);
if (!Array.isArray(listing.workflow_runs) || !Number.isSafeInteger(listing.total_count) ||
listing.total_count < 0 || listing.total_count > 100 || listing.workflow_runs.length !== listing.total_count) {
throw new Error("Cloud readiness run listing is incomplete.");
}
const run = listing.workflow_runs.filter((candidate) => trustedRun(candidate, sha, workflow.id))
.sort((a, b) => b.id - a.id)[0];
if (!run) return undefined;
// Attempt-specific jobs prevent an earlier successful attempt from blessing
// a later rerun. Keep pagination even though today's matrix fits one page.
const jobs = [];
for (let page = 1; page <= 10; page += 1) {
const batch = await api(`/repos/${repository}/actions/runs/${run.id}/attempts/${run.run_attempt}/jobs?per_page=100&page=${page}`);
if (!Array.isArray(batch.jobs)) throw new Error("Cloud readiness job listing is malformed.");
jobs.push(...batch.jobs);
if (batch.jobs.length < 100) break;
if (page === 10) throw new Error("Cloud readiness job listing is incomplete.");
}
const matches = jobs.filter((job) => job.name === sourceVerificationJob);
if (matches.length > 1) throw new Error("Cloud source verification job is ambiguous.");
const job = matches[0];View on GitHub (pinned to 3f1d897a7c)