How to fix invite_not_found: Invite has expired or already been used in Supabase

SupabaseINTERMEDIATEMEDIUMUpdated Dec 12, 2025· 178 views

Supabase returns 'invite_not_found: Invite has expired or already been used' when a user attempts to accept an email invitation that is no longer valid. This occurs when the invitation token has expired (default 24-48 hours), has already been redeemed, or was manually revoked. The fix involves resending a fresh invitation, extending the token expiry, or checking for duplicate acceptances.

Quick fix

Fixes invite_not_found

// Using the Supabase Management API
import { createClient } from '@supabase/supabase-js'

const supabaseAdmin = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
)

// Resend invitation to user email
const { data, error } = await supabaseAdmin.auth.admin.inviteUserByEmail(
  'user@example.com',
  {
    redirectTo: 'https://yourapp.com/invite-accepted',
    data: { role: 'member' } // Optional metadata
  }
)

if (error) {
  console.error('Failed to resend invitation:', error)
} else {
  console.log('New invitation sent:', data)
}
  1. Resend a fresh invitation
  2. Check invitation status and expiry
  3. Extend invitation expiry (if possible)
  4. Handle duplicate acceptance attempts
supabase — error
// Using the Supabase Management API
import { createClient } from '@supabase/supabase-js'
const supabaseAdmin = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
)
// Resend invitation to user email
const { data, error } = await supabaseAdmin.auth.admin.inviteUserByEmail(
'user@example.com',
{
redirectTo: 'https://yourapp.com/invite-accepted',
data: { role: 'member' } // Optional metadata
}
)
if (error) {
console.error('Failed to resend invitation:', error)
} else {
console.log('New invitation sent:', data)
}
invite_not_found: Invite has expired or already been used

What this error means

Supabase's email invitation system creates a time-limited, single-use token that allows users to join your organization or project. When someone clicks the invitation link, Supabase validates the token against the `auth.invitations` table (or similar internal tracking). The "invite_not_found" error means: 1. **Token expired**: Invitations typically expire after 24-48 hours for security reasons 2. **Already redeemed**: The same invitation link was used previously 3. **Revoked manually**: An admin removed the invitation before acceptance 4. **Database mismatch**: The invitation record was deleted or belongs to a different Supabase project This is a security feature preventing stale or reused invitations from granting access. The error appears during the final acceptance step, not when sending the invitation.

Typical symptoms

  • User clicks an invitation email link and sees HTTP 400 with "invite_not_found: Invite has expired or already been used"
  • The Supabase dashboard shows the invitation as 'pending' but the acceptance fails
  • Organization member list does not update after the user attempts to join
  • Browser console shows failed POST to `/auth/v1/invite/accept` endpoint
  • User remains outside the organization/project despite clicking the invitation link

Common causes

  1. The invitation was sent more than 48 hours ago and the token expired automatically
  2. The same invitation link was clicked twice (by the same user or different users)
  3. An organization admin manually revoked the invitation before acceptance
  4. The invitation was created in a different Supabase project/environment
  5. Database row was deleted from `auth.invitations` table (if using custom tracking)
  6. Clock skew between servers causes premature token expiration

How to fix "invite_not_found: Invite has expired or already been used"

1Resend a fresh invitation

The simplest fix is to send a new invitation from the Supabase dashboard or via the Management API:

javascript
// Using the Supabase Management API
import { createClient } from '@supabase/supabase-js'

const supabaseAdmin = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
)

// Resend invitation to user email
const { data, error } = await supabaseAdmin.auth.admin.inviteUserByEmail(
  'user@example.com',
  {
    redirectTo: 'https://yourapp.com/invite-accepted',
    data: { role: 'member' } // Optional metadata
  }
)

if (error) {
  console.error('Failed to resend invitation:', error)
} else {
  console.log('New invitation sent:', data)
}

In the Supabase dashboard:
1. Go to Authentication → Users
2. Find the user or click Invite User
3. Enter the email and customize the redirect URL
4. Click Send Invitation

The new token will be valid for another 24-48 hours.

2Check invitation status and expiry

Verify the current state of invitations using the Management API:

javascript
// List all pending invitations
const { data: invitations, error } = await supabaseAdmin
  .from('auth.invitations') // Internal table name may vary
  .select('*')
  .eq('email', 'user@example.com')
  .eq('status', 'pending')

if (error) {
  console.error('Error checking invitations:', error)
} else if (invitations.length === 0) {
  console.log('No pending invitations found')
} else {
  console.log('Found invitations:', invitations)

  // Check expiry
  invitations.forEach(invite => {
    const createdAt = new Date(invite.created_at)
    const now = new Date()
    const hoursDiff = (now.getTime() - createdAt.getTime()) / (1000 * 60 * 60)

    if (hoursDiff > 48) {
      console.log('Invitation expired ' + hoursDiff.toFixed(1) + ' hours ago')
    }
  })
}

Note: The exact table name and schema may vary. Supabase may store invitations in an internal system table not directly queryable.

3Extend invitation expiry (if possible)

For self-hosted or enterprise setups, you can adjust the invitation token lifetime:

sql
-- If using custom invitation tracking
UPDATE auth.invitations
SET expires_at = NOW() + INTERVAL '7 days'
WHERE email = 'user@example.com'
  AND status = 'pending';

-- For Supabase hosted, check project settings
-- Go to Project Settings → Authentication → Email Templates
-- Look for "Invitation validity period" or similar

Important: Extending expiry reduces security. Consider:
- Business needs vs security trade-offs
- Whether to implement manual approval workflows
- Adding MFA for invitation acceptance

4Handle duplicate acceptance attempts

Prevent users from clicking invitation links multiple times:

javascript
// Frontend: disable button after click
function handleInviteAccept(inviteToken) {
  const acceptButton = document.getElementById('accept-invite')
  acceptButton.disabled = true
  acceptButton.textContent = 'Processing...'

  // Call Supabase accept endpoint
  fetch('https://' + process.env.NEXT_PUBLIC_SUPABASE_URL + '/auth/v1/invite/accept', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ token: inviteToken }),
  })
  .then(response => {
    if (!response.ok) {
      if (response.status === 400) {
        // Show user-friendly message
        showError('This invitation has already been used. Please contact an administrator for a new one.')
      }
      acceptButton.disabled = false
      acceptButton.textContent = 'Accept Invitation'
    }
    return response.json()
  })
}

Also implement server-side checks to prevent race conditions.

Advanced notes

### How Supabase invitation tokens work

Supabase generates a cryptographically signed JWT token for each invitation, containing:
- Target email address
- Inviter's user ID
- Organization/project ID
- Expiration timestamp (typically 24-48 hours)
- Single-use flag

When the user clicks the link, Supabase:
1. Validates the JWT signature
2. Checks expiration timestamp against current time
3. Verifies the token hasn't been used before
4. Creates the user account and adds them to the organization

### Security considerations

Short expiry periods (24-48 hours) are a security feature:
- Prevents stolen email links from being used later
- Limits damage from accidental sharing
- Encourages prompt acceptance

For critical systems, consider:
- Manual approval workflows instead of auto-accept
- Requiring MFA before organization join
- Audit logs for all invitation events

### Custom invitation systems

If Supabase's built-in invitations don't meet your needs, you can build a custom system:
- Store invitations in your own table with longer expiry
- Implement your own token generation/validation
- Add additional metadata (roles, permissions, etc.)
- Create admin interfaces for managing invitations

Frequently asked questions

What does "invite_not_found" mean?
Supabase's email invitation system creates a time-limited, single-use token that allows users to join your organization or project. invitations table (or similar internal tracking). The "invite_not_found" error means: 1.
How do I fix "invite_not_found"?
Supabase returns 'invite_not_found: Invite has expired or already been used' when a user attempts to accept an email invitation that is no longer valid. This occurs when the invitation token has expired (default 24-48 hours), has already been redeemed, or was manually revoked. The fix involves resending a fresh invitation, extending the token expiry, or checking for duplicate acceptances.
What causes "invite_not_found"?
The invitation was sent more than 48 hours ago and the token expired automatically The same invitation link was clicked twice (by the same user or different users) An organization admin manually revoked the invitation before acceptance
Is the "invite_not_found" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading