{"record":{"id":"d88085587638d8e6","repo":"Significant-Gravitas/AutoGPT","slug":"invitation-has-expired","errorCode":null,"errorMessage":"Invitation has expired","messagePattern":"Invitation has expired","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"autogpt_platform/backend/backend/api/features/orgs/invitation_routes.py","lineNumber":157,"sourceCode":"@router.post(\n    \"/{token}/accept\",\n    summary=\"Accept invitation\",\n    tags=[\"invitations\"],\n    dependencies=[Security(requires_user)],\n)\nasync def accept_invitation(\n    token: str,\n    user_id: Annotated[str, Security(get_user_id)],\n) -> dict:\n    invitation = await prisma.orginvitation.find_unique(where={\"token\": token})\n    if invitation is None:\n        raise NotFoundError(\"Invitation not found\")\n    if invitation.acceptedAt is not None:\n        raise HTTPException(400, detail=\"Invitation already accepted\")\n    if invitation.revokedAt is not None:\n        raise HTTPException(400, detail=\"Invitation has been revoked\")\n    if invitation.expiresAt < datetime.now(timezone.utc):\n        raise HTTPException(400, detail=\"Invitation has expired\")\n\n    # Verify the accepting user's email matches the invitation\n    accepting_user = await prisma.user.find_unique(where={\"id\": user_id})\n    if accepting_user is None:\n        raise HTTPException(401, detail=\"User not found\")\n    if accepting_user.email.lower() != invitation.email.lower():\n        raise HTTPException(\n            403,\n            detail=\"This invitation was sent to a different email address\",\n        )\n\n    # Add user to org (idempotent — handles race condition from concurrent accepts)\n    try:\n        await org_db.add_org_member(\n            org_id=invitation.orgId,\n            user_id=user_id,\n            is_admin=invitation.isAdmin,\n            is_billing_manager=invitation.isBillingManager,","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/backend/backend/api/features/orgs/invitation_routes.py#L139-L175","documentation":"Raised by POST /api/invitations/{token}/accept when the current UTC time is past the invitation's expiresAt (created as now + INVITATION_TTL_DAYS at create time). The token is time-limited by design. HTTP 400.","triggerScenarios":"User clicks an invitation link older than the TTL (INVITATION_TTL_DAYS), or a system clock skew makes now() appear past expiry; accepting a link days after the email arrived.","commonSituations":"User leaves invitation emails unread in the inbox; onboarding emails queued in spam for days; long-lived bookmarked links; server clock drift.","solutions":["Ask the admin to send a fresh invitation — expired tokens cannot be extended.","If you administer the backend and truly need a longer window, adjust INVITATION_TTL_DAYS and regenerate; otherwise leave the default.","Client: compare the expiry shown in the invitation payload (GET pending) with current time before offering the Accept button.","Check server clock synchronization (NTP) if you believe the invitation should still be valid."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const inv = await api.get('/api/invitations/pending').then(r => r.data.find(i => i.token === token));\nif (inv && new Date(inv.expiresAt) < new Date()) {\n  show('This invitation has expired. Ask for a new one.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.post(`/api/invitations/${token}/accept`);\n} catch (e) {\n  if (e.status === 400 && e.detail === 'Invitation has expired') { show('Invitation expired'); return; }\n  throw e;\n}","preventionTips":["Check expiresAt client-side before offering the accept action","Remind users invitation links have a TTL","Keep server clocks NTP-synced"],"tags":["invitations","expiry","ttl","http-400"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}