Significant-Gravitas/AutoGPT · warning · HTTPException
Invitation already accepted
Error message
Invitation already accepted
What it means
Raised by POST /api/invitations/{token}/accept when the invitation exists but acceptedAt is already set — the invitation was consumed by a previous accept. Acceptance is single-use; the membership add itself is idempotent, but the state check rejects a second accept of the same token. HTTP 400.
Source
Thrown at autogpt_platform/backend/backend/api/features/orgs/invitation_routes.py:153
# --- Token-based endpoints (under /api/invitations) ---
@router.post(
"/{token}/accept",
summary="Accept invitation",
tags=["invitations"],
dependencies=[Security(requires_user)],
)
async def accept_invitation(
token: str,
user_id: Annotated[str, Security(get_user_id)],
) -> dict:
invitation = await prisma.orginvitation.find_unique(where={"token": token})
if invitation is None:
raise NotFoundError("Invitation not found")
if invitation.acceptedAt is not None:
raise HTTPException(400, detail="Invitation already accepted")
if invitation.revokedAt is not None:
raise HTTPException(400, detail="Invitation has been revoked")
if invitation.expiresAt < datetime.now(timezone.utc):
raise HTTPException(400, detail="Invitation has expired")
# Verify the accepting user's email matches the invitation
accepting_user = await prisma.user.find_unique(where={"id": user_id})
if accepting_user is None:
raise HTTPException(401, detail="User not found")
if accepting_user.email.lower() != invitation.email.lower():
raise HTTPException(
403,
detail="This invitation was sent to a different email address",
)
# Add user to org (idempotent — handles race condition from concurrent accepts)
try:
await org_db.add_org_member(View on GitHub (pinned to 9c8bb5550f)
Solutions
- Treat this 400 as success if the user is already a member — verify with GET org members or the pending-invitations endpoint.
- Make the client idempotent: disable the accept button on first click and do not auto-retry POSTs.
- If UI should allow re-entry, redirect to the org instead of showing an error when this message is returned.
Example fix
// before
await api.post(`/api/invitations/${token}/accept`); // throws on second click
// after
try {
await api.post(`/api/invitations/${token}/accept`);
} catch (e) {
if (e.status === 400 && e.detail === 'Invitation already accepted') {
router.push('/dashboard'); // user is already a member
return;
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
const me = await api.get('/api/invitations/pending').then(r => r.data);
const stillPending = me.some(i => i.token === token); Try / catch
try {
await api.post(`/api/invitations/${token}/accept`);
} catch (e) {
if (e.status === 400 && e.detail === 'Invitation already accepted') {
router.push('/dashboard'); return; // already a member
}
throw e;
} Prevention
- Disable the accept button on first click
- Never auto-retry non-idempotent POSTs
- Check pending-invitations before re-showing accept UI
When it happens
Trigger: User clicks the accept link twice (double-click, page reload after accept, email link reopened), or two browser tabs race to accept. The first request sets acceptedAt; subsequent requests get this 400.
Common situations: Frontend retries the POST after a network hiccup that actually succeeded; user forwards the email and another acceptance already happened; back-button resubmission.
Related errors
- Invitation has been revoked
- Invitation already revoked
- Teams not found in this organization: {invalid}
- Invitation has expired
- PKCE verifier not found in session
AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14).
Data as JSON: /api/errors/b56480a3981e3bec.
Report an issue: GitHub.