eyaltoledano/claude-task-master · error · AuthenticationError
INVALID_MFA_CODE
INVALID_MFA_CODE
Error message
Invalid MFA code: ${error.message} What it means
SupabaseClient.verifyMFA() throws this when `client.auth.mfa.verify({factorId, challengeId, code})` returns an error — Supabase rejected the TOTP code (or the challenge). It is surfaced as AuthenticationError with code INVALID_MFA_CODE, distinguishing a wrong/expired code from infrastructure failures.
Source
Thrown at packages/tm-core/src/modules/integration/clients/supabase-client.ts:567
await client.auth.mfa.challenge({ factorId });
if (challengeError || !challengeData) {
throw new AuthenticationError(
`Failed to create MFA challenge: ${challengeError?.message || 'Unknown error'}`,
'MFA_VERIFICATION_FAILED'
);
}
// Verify the TOTP code
const { data, error } = await client.auth.mfa.verify({
factorId,
challengeId: challengeData.id,
code
});
if (error) {
this.logger.error('MFA verification failed:', error);
throw new AuthenticationError(
`Invalid MFA code: ${error.message}`,
'INVALID_MFA_CODE'
);
}
if (!data) {
throw new AuthenticationError(
'No data returned from MFA verification',
'INVALID_RESPONSE'
);
}
// After successful MFA verification, refresh the session to get the upgraded AAL2 session
const {
data: { session },
error: refreshError
} = await client.auth.refreshSession();
View on GitHub (pinned to c0c98d367c)
Solutions
- Ask the user to re-enter the current 6-digit code from the authenticator app (prompt for a fresh challenge on retry).
- Create a new challenge before each verification attempt — challenges expire; never reuse a challengeId.
- Check authenticator device clock sync (TOTP depends on accurate time).
- Verify the code is submitted promptly after the challenge is created; log error.message for specific Supabase codes.
Example fix
// before
const { data: c } = await supabase.auth.mfa.challenge({ factorId });
await client.verifyMFA(factorId, code); // may reuse stale challenge internally
// after
let ok = false;
for (let attempt = 0; attempt < 3 && !ok; attempt++) {
const code = await promptForTotp(); // fresh code each attempt
try { await client.verifyMFA(factorId, code); ok = true; }
catch (e) { if (e.code !== 'INVALID_MFA_CODE') throw e; }
} Defensive patterns
Strategy: retry
Validate before calling
// guard before submitting
if (!/^[0-9]{6}$/.test(code)) throw new Error('TOTP code must be 6 digits');
// prompt immediately after challenge creation so the code is still in its validity window Type guard
function isSixDigitCode(c: unknown): c is string {
return typeof c === 'string' && /^[0-9]{6}$/.test(c);
} Try / catch
for (let attempt = 0; attempt < 3; attempt++) {
try {
await client.verifyMFA(factorId, await promptTotp());
break;
} catch (e) {
if (e instanceof AuthenticationError && e.code === 'INVALID_MFA_CODE') {
console.error('Incorrect or expired code — try the current 6-digit code');
continue; // new prompt (and new challenge)
}
throw e;
}
} Prevention
- Prompt for and submit the code promptly after the challenge is created.
- Never reuse a TOTP code or a consumed challenge across attempts.
- Ask users to check authenticator device clock sync on repeated failures.
- Limit retries (2-3) and surface a clear 'wrong code' message each time.
When it happens
Trigger: User enters a wrong 6-digit TOTP code; code is reused (same code used in the previous 30s window); code entered too slowly (past the validity window); challenge expired between challenge() and verify(); clock drift on the user's authenticator device.
Common situations: Typo'd or stale authenticator code; copying an old code from an authenticator app list; system clock skew on phone or machine; prompting for MFA, waiting (e.g. CI job paused), then submitting after challenge expiry.
Related errors
- MFA_VERIFICATION_FAILED
- MFA_REQUIRED
- MFA_VERIFICATION_FAILED
- MFA_VERIFICATION_FAILED
- Invalid maxAttempts value: ${maxAttempts}. Must be a positiv
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/7b0480b17c053235.
Report an issue: GitHub.