paperclipai/paperclip · error · Error
Cloud source verification did not pass for
Error message
Cloud source verification did not pass for ${sha} (run ${run.id}, attempt ${run.run_attempt}). Rerun Cloud readiness before retrying the release. What it means
When the selected run attempt finished (or the matched job completed) but not with conclusion 'success' for this sha/attempt, the script throws this definitive failure. Unlike timeouts (which return undefined and keep polling), a completed non-success run means the verification answer is 'no' and the release must not retry until Cloud readiness is rerun.
Solutions
- Open the run link pattern https://github.com/paperclipai/paperclip/actions/runs/{runId}/attempts/{attempt} and read why the job failed (logs of 'Cloud source verified v1').
- Fix the failing source check in the repo, push, and let a fresh push-triggered Cloud readiness run complete on the new sha.
- Rerun Cloud readiness ('Re-run all jobs') so a new successful attempt exists for the same sha, then retry the release.
- Confirm the job named 'Cloud source verified v1' still exists in the workflow; if renamed, restore the exact versioned name.
Example fix
# before: skipping the failing check locally git push origin HEAD # after: verify locally before pushing pnpm -r typecheck && pnpm test:run && git push origin HEAD
Defensive patterns
Strategy: try-catch
Type guard
function isPassing(job, sha, run) {
return job?.status === 'completed' && job?.conclusion === 'success' &&
job?.head_sha === sha && job?.run_id === run.id && job?.run_attempt === run.run_attempt;
} Try / catch
try {
const proof = await waitForSourceVerification(sha, { api, timeoutMs });
} catch (e) {
if (/Cloud source verification did not pass/.test(e.message)) {
const [, runId, attempt] = e.message.match(/run (\d+), attempt (\d+)/) ?? [];
console.error(`Inspect https://github.com/paperclipai/paperclip/actions/runs/${runId}/attempts/${attempt}, fix, rerun Cloud readiness.`);
}
} Prevention
- Run the same checks locally (pnpm -r typecheck && pnpm test:run) before pushing, so Cloud readiness passes on the first attempt.
- Watch the Cloud readiness run after pushing; fix failures before starting the release poll.
- Never cancel the workflow run for a sha you are about to release against.
- Keep the 'Cloud source verified v1' job present and green on every push to master.
When it happens
Trigger: The Cloud source verified v1 job completed with conclusion failure/cancelled/timed_out, or the run itself completed without a passing verification job; then the next readSourceVerification poll throws with the run id and attempt in the message.
Common situations: Cloud readiness failed on a lint/test step in CI; the workflow was cancelled; an earlier failed attempt is being observed after someone triggered a rerun but the old attempt completed first; a workflow edit removed or renamed the verification job so it never succeeds.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Cloud source verification timed out for
- Migrator producers failed
- Cloud artifacts timed out for
- Cloud readiness job listing is incomplete.
- Cloud readiness job listing is malformed.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/f080b4f62949f0ba.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloud-source-verification.mjs:59
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];
if (job?.status === "completed" && job.conclusion === "success" &&
job.head_sha === sha && job.run_id === run.id && job.run_attempt === run.run_attempt) {
// Re-read the run after its jobs: a rerun that started during polling must
// not let the previous attempt through. Changes are retried next poll.
const current = await api(`/repos/${repository}/actions/runs/${run.id}`);
if (!trustedRun(current, sha, workflow.id) || current.run_attempt !== run.run_attempt) return undefined;
return { sha, runId: run.id, attempt: run.run_attempt, jobId: job.id };
}
if (job?.status === "completed" || run.status === "completed") {
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.`);
}
return undefined;
}
// Statuses that say "ask again", not "the answer is no". A release must not be
// blocked because GitHub returned a gateway error during a 45-minute poll.
const TRANSIENT_READ_STATUSES = new Set([408, 425, 429, 500, 502, 503, 504]);
// A rate-limited read also says "ask again", and GitHub reports both primary
// and secondary rate limits as 403. Only the headers separate that from a
// token that may not read Actions, which must still fail at once.
function rateLimited(response) {
if (response.status !== 403) return false;
const header = (name) => response.headers?.get?.(name) ?? null;
return header("retry-after") !== null || header("x-ratelimit-remaining") === "0";
}
/** Milliseconds from a Retry-After header, when it carries a sane delay. */View on GitHub (pinned to 3f1d897a7c)