{"record":{"id":"35c85117da3b3bc2","repo":"Hmbown/CodeWhale","slug":"github-release-asset-set-is-stale-for-workflow-run-run","errorCode":null,"errorMessage":"GitHub Release asset set is stale for workflow run ${run.database_id || run.id}: ${stale.join(\"; \")}","messagePattern":"GitHub Release asset set is stale for workflow run (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"npm/codewhale/scripts/verify-release-assets.js","lineNumber":293,"sourceCode":"  // job baseline is unavailable.\n  const baseline = run.release_job_started_at || run.run_started_at || run.created_at;\n  const baselineLabel = run.release_job_started_at ? \"release job start\" : \"workflow run start\";\n  const freshnessBaseline = parseGitHubTime(baseline, baselineLabel);\n  const stale = [];\n  for (const expected of expectedAssets) {\n    const asset = assetsByName.get(expected);\n    if (asset.state && asset.state !== \"uploaded\") {\n      stale.push(`${expected} has state ${asset.state}`);\n      continue;\n    }\n    const updatedAt = parseGitHubTime(asset.updated_at || asset.created_at, `${expected} update`);\n    if (updatedAt < freshnessBaseline) {\n      stale.push(`${expected} updated at ${asset.updated_at || asset.created_at}`);\n    }\n  }\n\n  if (stale.length > 0) {\n    throw new Error(\n      `GitHub Release asset set is stale for workflow run ${run.database_id || run.id}: ${stale.join(\"; \")}`,\n    );\n  }\n}\n\nasync function verifyGitHubReleaseFreshness(repo, version, expectedAssets) {\n  const tag = `v${version}`;\n  const tagSha = await resolveTagCommitSha(repo, tag);\n  const release = await githubApi(repo, `/releases/tags/${encodeURIComponent(tag)}`);\n  const run = await findReleaseWorkflowRun(repo, tag, tagSha);\n  assertReleaseAssetsFresh(release, expectedAssets, run);\n  console.log(\n    `GitHub release asset freshness OK: ${expectedAssets.length} release assets for ${tag} were produced by run ${run.database_id || run.id} at ${tagSha.slice(0, 12)}.`,\n  );\n}\n\nfunction parseChecksumManifest(text) {\n  const checksums = new Map();","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/npm/codewhale/scripts/verify-release-assets.js#L275-L311","documentation":"After confirming all expected assets exist, assertReleaseAssetsFresh compares each asset's updated_at against the freshness baseline (the successful release job's started_at, falling back to run timestamps). Assets older than the baseline are considered stale and it throws listing each stale asset with its timestamp.","triggerScenarios":"An asset's updatedAt < freshnessBaseline — i.e., the release asset predates the workflow run that should have produced it, indicating leftovers from an earlier run or a re-tagged release.","commonSituations":"Re-tagging a version without deleting the old release, so old assets survive; manually attaching binaries built from a different commit; concurrency skipping upload while old assets remain.","solutions":["Delete the release (or its assets) and re-run the Release workflow so all assets are freshly built and uploaded.","Ensure the release job started before asset upload timestamps (check clock skew if times are close).","Never re-tag an existing version; use a new version string."],"exampleFix":"// before\ngit tag -f v1.2.3 HEAD~1  # stale assets kept\n// after\ngh release delete v1.2.3 --cleanup-tag && git tag v1.2.3 <sha> && gh workflow run release.yml","handlingStrategy":"validation","validationCode":"const baseline = Date.parse(run.release_job_started_at ?? run.run_started_at); const stale = expected.filter(n => Date.parse(assetByName(n).updated_at) < baseline); if (stale.length) console.error(\"Stale assets:\", stale);","typeGuard":"null","tryCatchPattern":"try { verifyFreshness(); } catch (e) { if (e.message.includes(\"asset set is stale\")) { console.error(\"Delete the release and re-run the Release workflow for this tag.\"); process.exit(1); } throw e; }","preventionTips":["Never re-tag an existing version; bump the version instead","Delete old releases before re-releasing the same tag","Build and upload assets only inside the release job"],"tags":["release","assets","stale-artifacts","github"],"backgroundTag":"checksum-mismatch","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}