paperclipai/paperclip · error · Error
Cloud readiness workflow identity does not match.
Error message
Cloud readiness workflow identity does not match.
What it means
readSourceVerification looks up the cloud-readiness workflow via the GitHub API and requires the returned workflow object to have path exactly '.github/workflows/cloud-readiness.yml' and a positive safe-integer id. If GitHub resolves a workflow with a different path or an invalid id, this throws — guarding against renamed/moved workflow files or unexpected API responses.
Solutions
- Restore the workflow to .github/workflows/cloud-readiness.yml or update the workflowPath constant in scripts/cloud-source-verification.mjs to the new path.
- Inspect `GET /repos/paperclipai/paperclip/actions/workflows/cloud-readiness.yml` output to see what path/id GitHub actually returns.
- Fix test doubles so the api() stub returns { path, id } matching the expected workflow.
Example fix
// before const workflowPath = ".github/workflows/cloud-readiness.yml"; // file renamed to cloud-ready.yml // after const workflowPath = ".github/workflows/cloud-ready.yml"; // keep constant in sync with the actual file path
Defensive patterns
Strategy: try-catch
Validate before calling
const wf = await api("/repos/paperclipai/paperclip/actions/workflows/cloud-readiness.yml");
if (wf?.path !== ".github/workflows/cloud-readiness.yml") {
throw new Error(`Workflow file moved: ${wf?.path}. Update workflowPath in cloud-source-verification.mjs.`);
} Try / catch
try {
const proof = await waitForSourceVerification(sha, { api });
} catch (error) {
if (/workflow identity does not match/.test(error.message)) {
console.error("cloud-readiness.yml path/id changed — sync the constant and rerun.");
process.exitCode = 1;
} else throw error;
} Prevention
- Treat .github/workflows/cloud-readiness.yml as a pinned contract; rename only with a coordinated script change
- Test readSourceVerification with realistic workflow objects (path + positive id)
- Check the API response after any workflow refactoring in .github/workflows/
When it happens
Trigger: The workflow file was renamed/moved (workflow.path differs), GitHub returns a workflow entry with a missing/zero/negative id, or a same-named workflow at a different path is resolved.
Common situations: Refactoring .github/workflows/cloud-readiness.yml to another filename; having two workflows and the API resolving the wrong one by name; mocked api() fixtures missing id/path fields.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- GitHub webhook configuration is incomplete
- github_webhook_recovery_invalid_input
- github_webhook_recovery_invalid_response
- A full lowercase source SHA is required.
- A reusable lease cannot be replaced and reacquired in the…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/81474c6a6205ece8.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloud-source-verification.mjs:26
function assertSha(sha) {
if (!/^[a-f0-9]{40}$/.test(sha ?? "")) throw new Error("A full lowercase source SHA is required.");
}
function trustedRun(run, sha, workflowId) {
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;View on GitHub (pinned to 3f1d897a7c)