{"record":{"id":"fcbd93aa2eb8b7a2","repo":"nexu-io/open-design","slug":"authorized-team-project-pull-receipt-expired","errorCode":"AUTHORIZED_TEAM_PROJECT_PULL_RECEIPT_EXPIRED","errorMessage":"authorized pull receipt is stale","messagePattern":"authorized pull receipt is stale","errorType":"exception","errorClass":"AuthorizedTeamProjectPullReceiptExpiredError","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/collab/authorized-team-project-pull.ts","lineNumber":180,"sourceCode":"    !MANIFEST_DIGEST_PATTERN.test(receipt.manifestDigest) ||\n    receipt.lifecycleState !== 'active' ||\n    receipt.ownerMemberId === receipt.viewerMemberId\n  ) {\n    throw new Error('authorized pull receipt binding is incomplete');\n  }\n  const authorizedAt = Date.parse(receipt.authorizedAt);\n  const expiresAt = Date.parse(receipt.expiresAt);\n  const nowMs = input.nowMs ?? Date.now();\n  if (\n    !Number.isFinite(authorizedAt) ||\n    !Number.isFinite(expiresAt) ||\n    expiresAt <= authorizedAt ||\n    expiresAt - authorizedAt > RECEIPT_MAX_AGE_MS\n  ) {\n    throw new Error('authorized pull receipt is stale');\n  }\n  if (nowMs >= expiresAt) {\n    throw new AuthorizedTeamProjectPullReceiptExpiredError(\n      'authorized pull receipt is stale',\n    );\n  }\n}\n\nexport function isAuthorizedTeamProjectPullUnavailable(\n  error: unknown,\n): boolean {\n  const message = error instanceof Error ? error.message : String(error);\n  return /unknown command [\"']?pull[\"']?.*team-projects/iu.test(message) ||\n    /unknown command [\"']?team-projects[\"']?/iu.test(message) ||\n    /unknown flag:\\s*--(?:expected-version|live-dir|ref|json)\\b/iu.test(message);\n}\n\n/** The packaged CLI predates `team-projects pull --authorize-only` (or lacks\n *  `team-projects pull` entirely). Callers must fail OPEN — pull as before —\n *  never block materialization on a missing probe. */\nexport function isAuthorizedTeamProjectPullInspectUnavailable(","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/collab/authorized-team-project-pull.ts#L162-L198","documentation":"Thrown as AuthorizedTeamProjectPullReceiptExpiredError (code AUTHORIZED_TEAM_PROJECT_PULL_RECEIPT_EXPIRED, detected via isAuthorizedTeamProjectPullReceiptExpired) when the current time (nowMs, default Date.now()) has passed the receipt's expiresAt. This is the typed, retryable expiry path, distinct from the plain Error 'authorized pull receipt is stale' at line 177 which covers malformed dates or an authorization window longer than RECEIPT_MAX_AGE_MS (2s). The receipt is a short-lived bearer of authorization and must be consumed promptly.","triggerScenarios":"Clock skew between the machine that authorized the pull and the daemon consuming it, processing latency or a paused/debugged process that exceeded the receipt's lifetime, or a replay attempt using an old receipt.","commonSituations":"System clock drift, a debugger breakpoint held the process past expiry, a queued/retried pull using a stale receipt, or heavy load stretching the authorize-to-pull gap beyond the 2-second window.","solutions":["Re-request a fresh authorized pull — do not retry with the same receipt.","Sync the system clock (NTP) on both the authorizer and the daemon to remove skew.","Ensure the pull happens immediately after authorization; remove artificial delays or long queues.","Detect this case with isAuthorizedTeamProjectPullReceiptExpired(error) and trigger re-authorization in the caller."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Re-authorize when the receipt would already be expired by the time it is consumed.\nfunction receiptWillBeFresh(receipt: { authorizedAt: string; expiresAt: string }, now = Date.now()): boolean {\n  const expiresAt = Date.parse(receipt.expiresAt);\n  return Number.isFinite(expiresAt) && now < expiresAt;\n}","typeGuard":"import { isAuthorizedTeamProjectPullReceiptExpired } from './authorized-team-project-pull.js';\n// isAuthorizedTeamProjectPullReceiptExpired(error) is the provided guard","tryCatchPattern":"try {\n  await runAuthorizedPull(args, workspaceId, options);\n} catch (err) {\n  if (isAuthorizedTeamProjectPullReceiptExpired(err)) {\n    // re-request a fresh authorized pull — never reuse the stale receipt\n    await runAuthorizedPull(args, workspaceId, options);\n    return;\n  }\n  throw err;\n}","preventionTips":["Consume the receipt immediately after authorization; the window is ~2 seconds.","Keep system clocks synced (NTP) on authorizer and consumer.","Detect expiry with isAuthorizedTeamProjectPullReceiptExpired and re-authorize rather than retrying the same receipt.","Avoid pausing the process between authorize and pull (debugger breakpoints, long queues)."],"tags":["collab","vela","receipt","security","expiry","team-projects","retryable"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}