medusajs/medusa · error · MedusaError
Invalid token
Error message
Invalid token
What it means
Thrown by consumePasswordResetToken when no stored reset token matches the hash of the submitted jti. UNAUTHORIZED: the token is unknown — never issued, already consumed, or corrupted.
Source
Thrown at packages/modules/auth/src/services/auth-module.ts:925
data: AuthTypes.ConsumePasswordResetTokenDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<AuthTypes.ConsumePasswordResetTokenResponse> {
return await this.consumePasswordResetToken_(data, sharedContext)
}
@InjectTransactionManager()
protected async consumePasswordResetToken_(
data: AuthTypes.ConsumePasswordResetTokenDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<AuthTypes.ConsumePasswordResetTokenResponse> {
const [resetToken] = await this.authPasswordResetTokenService_.list(
{ token_hash: this.hashVerificationToken_(data.jti) },
{},
sharedContext
)
if (!resetToken) {
throw new MedusaError(MedusaError.Types.UNAUTHORIZED, "Invalid token")
}
if (new Date(resetToken.expires_at).getTime() <= Date.now()) {
await this.authPasswordResetTokenService_.delete(
resetToken.id,
sharedContext
)
throw new MedusaError(MedusaError.Types.UNAUTHORIZED, "Invalid token")
}
const providerIdentity = await this.providerIdentityService_.retrieve(
resetToken.provider_identity_id,
{},
sharedContext
)
if (
providerIdentity.provider !== data.provider ||View on GitHub (pinned to 5e06e544a2)
Solutions
- Treat as consumed/invalid: restart the reset flow with a new token request
- Ensure the token value is passed intact (no URL-encoding truncation)
- Verify environment/database consistency between issuance and consumption
Example fix
// before
await authModule.consumePasswordResetToken({ jti: token, entity_id, provider })
// after
const result = await authModule.consumePasswordResetToken({ jti: token, entity_id, provider }).catch((e) => {
if (e.type === 'unauthorized') throw new Error('Reset link invalid or already used — request a new one')
throw e
}) Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try { await authModule.consumePasswordResetToken(input) } catch (e) { if (e.type === 'unauthorized') redirect('/reset?expired=1'); throw e } Prevention
- Never consume a token twice; consume once at confirmation
- Pass the jti intact from the link
When it happens
Trigger: Consuming a token twice (tokens are deleted after use); using a token from a different environment/database; malformed or truncated jti; token rows purged.
Common situations: User clicks an old reset link after already resetting; double-click on the confirm link; env drift between staging and production tokens.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- UNAUTHORIZED
- Provider identity with entity_id ${data.entity_id} and provi
- Customer with this email already has an account
- The user is already authenticated and cannot accept an invit
- User ID not found
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/1629192f2d6d24cf.
Report an issue: GitHub.