nexu-io/open-design · error · AuthorizedTeamProjectPullReceiptExpiredError
AUTHORIZED_TEAM_PROJECT_PULL_RECEIPT_EXPIRED
AUTHORIZED_TEAM_PROJECT_PULL_RECEIPT_EXPIRED
Error message
authorized pull receipt is stale
What it means
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.
Source
Thrown at apps/daemon/src/collab/authorized-team-project-pull.ts:180
!MANIFEST_DIGEST_PATTERN.test(receipt.manifestDigest) ||
receipt.lifecycleState !== 'active' ||
receipt.ownerMemberId === receipt.viewerMemberId
) {
throw new Error('authorized pull receipt binding is incomplete');
}
const authorizedAt = Date.parse(receipt.authorizedAt);
const expiresAt = Date.parse(receipt.expiresAt);
const nowMs = input.nowMs ?? Date.now();
if (
!Number.isFinite(authorizedAt) ||
!Number.isFinite(expiresAt) ||
expiresAt <= authorizedAt ||
expiresAt - authorizedAt > RECEIPT_MAX_AGE_MS
) {
throw new Error('authorized pull receipt is stale');
}
if (nowMs >= expiresAt) {
throw new AuthorizedTeamProjectPullReceiptExpiredError(
'authorized pull receipt is stale',
);
}
}
export function isAuthorizedTeamProjectPullUnavailable(
error: unknown,
): boolean {
const message = error instanceof Error ? error.message : String(error);
return /unknown command ["']?pull["']?.*team-projects/iu.test(message) ||
/unknown command ["']?team-projects["']?/iu.test(message) ||
/unknown flag:\s*--(?:expected-version|live-dir|ref|json)\b/iu.test(message);
}
/** The packaged CLI predates `team-projects pull --authorize-only` (or lacks
* `team-projects pull` entirely). Callers must fail OPEN — pull as before —
* never block materialization on a missing probe. */
export function isAuthorizedTeamProjectPullInspectUnavailable(View on GitHub (pinned to 5be4028344)
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.
Defensive patterns
Strategy: retry
Validate before calling
// Re-authorize when the receipt would already be expired by the time it is consumed.
function receiptWillBeFresh(receipt: { authorizedAt: string; expiresAt: string }, now = Date.now()): boolean {
const expiresAt = Date.parse(receipt.expiresAt);
return Number.isFinite(expiresAt) && now < expiresAt;
} Type guard
import { isAuthorizedTeamProjectPullReceiptExpired } from './authorized-team-project-pull.js';
// isAuthorizedTeamProjectPullReceiptExpired(error) is the provided guard Try / catch
try {
await runAuthorizedPull(args, workspaceId, options);
} catch (err) {
if (isAuthorizedTeamProjectPullReceiptExpired(err)) {
// re-request a fresh authorized pull — never reuse the stale receipt
await runAuthorizedPull(args, workspaceId, options);
return;
}
throw err;
} Prevention
- 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).
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- authorized pull receipt has invalid ${key}
- authorized pull response is not valid JSON
- authorized pull response must be an object
- WORKSPACE_RESOURCE_AUTHORITY_UNAVAILABLE
- authoritative Team resource listing is unavailable
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/fcbd93aa2eb8b7a2.
Report an issue: GitHub.