paperclipai/paperclip · error · Error
Cloud source verification job is ambiguous.
Error message
Cloud source verification job is ambiguous.
What it means
After collecting jobs for the selected attempt, the script filters by the exact job name 'Cloud source verified v1'. If more than one job on that attempt has that name, the script cannot tell which one proves source verification and throws. The job name is the unique contract for this check.
Solutions
- Open .github/workflows/cloud-readiness.yml and remove or rename the duplicated job so exactly one job is named 'Cloud source verified v1'.
- Check the run's job list (https://api.github.com/repos/paperclipai/paperclip/actions/runs/{id}/attempts/{n}/jobs) to see which duplicate exists and delete the wrong one from the workflow.
- If a reusable workflow causes the duplicate, change its job display name or call it once.
- Bump sourceVerificationJob (the versioned name) and the workflow together if a new contract name is intended.
Example fix
# before: two jobs with the same display name
verify-a:
name: Cloud source verified v1
verify-b:
name: Cloud source verified v1
# after
verify:
name: Cloud source verified v1 Defensive patterns
Strategy: validation
Validate before calling
const jobs = (await api(jobsPath)).jobs ?? [];
const matches = jobs.filter((j) => j.name === 'Cloud source verified v1');
if (matches.length > 1) throw new Error('duplicate verification job name in workflow'); Type guard
function isUniqueJobName(jobs, name) {
return jobs.filter((j) => j.name === name).length === 1;
} Try / catch
try {
const proof = await readSourceVerification(sha, api);
} catch (e) {
if (e.message.includes('job is ambiguous')) {
console.error('Two jobs named Cloud source verified v1 — fix cloud-readiness.yml.');
}
} Prevention
- Grep .github/workflows/cloud-readiness.yml for the job display name before merging workflow edits; it must appear exactly once.
- Never copy the verification job block without renaming the display name.
- When invoking reusable workflows multiple times, give each a distinct job name.
- Keep sourceVerificationJob and the workflow name in sync — both must change together.
When it happens
Trigger: The cloud-readiness workflow (or a reusable-workflow caller) defines or renames a step so two jobs on the same run attempt end up named 'Cloud source verified v1' — e.g. duplicating the verification job in a matrix, a merge accident adding a second job, or a reusable workflow including the job twice.
Common situations: Someone copies the verification job block in .github/workflows/cloud-readiness.yml; a reusable workflow is invoked twice with the same display name; a name-mangling action renames jobs unexpectedly.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Cloud artifacts timed out for
- Cloud readiness job listing is incomplete.
- Cloud readiness job listing is malformed.
- Cloud readiness run listing is incomplete.
- Cloud source verification did not pass for
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/a5950a411905c961.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloud-source-verification.mjs:48
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];
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]);View on GitHub (pinned to 3f1d897a7c)