mastra-ai/mastra · error
Invalid email or password
Error message
Invalid email or password
What it means
After a sign-in request that returned OK, signIn() checks that the parsed JSON body contains result.user. If Better Auth returned a 2xx response without a user object, the provider throws 'Invalid email or password'. This is a defensive fallback for malformed or empty success responses.
Source
Thrown at auth/better-auth/src/index.ts:765
async signIn(email: string, password: string, request: Request): Promise<CredentialsResult<EEUser>> {
const headers = request?.headers ?? new Headers();
// Use asResponse: true to get the full response with Set-Cookie headers
const response = await this.auth.api.signInEmail({
body: { email, password },
headers,
asResponse: true,
});
if (!response.ok) {
const errorData = (await response.json().catch(() => ({}))) as { message?: string };
throw new Error(errorData.message || 'Invalid email or password');
}
const result = (await response.json()) as { user?: User; token?: string | null };
if (!result?.user) {
throw new Error('Invalid email or password');
}
// Extract Set-Cookie headers from Better Auth response
const cookies: string[] = [];
const setCookieHeader = response.headers.get('set-cookie');
if (setCookieHeader) {
// Split multiple cookies (they may be comma-separated or in multiple headers)
cookies.push(...setCookieHeader.split(/,(?=\s*\w+=)/));
}
return {
user: mapBetterAuthUserToEEUser(result.user),
token: result.token ?? undefined,
cookies,
};
}
/**View on GitHub (pinned to 75dd419e61)
Solutions
- Check the installed better-auth version and its signInEmail response shape; align with the version this provider expects.
- Log the raw response body to see what the server actually returned on success.
- Fall back to the sign-in flow's error path: retry signIn with corrected credentials in case the server returned 200 for a non-authenticated session.
Example fix
// before: expecting { user } but better-auth v1.x returns different shape
const result = await auth.signIn({ email, password });
// after: pin/upgrade better-auth to the version whose signInEmail returns { user, token }
// package.json: "better-auth": "<version compatible with @mastra/auth-better-auth>" Defensive patterns
Strategy: type-guard
Validate before calling
const res = await providerSignInResponse;
if (res.ok) {
const body = await res.json();
if (!body?.user) console.warn('signIn returned 2xx without user; check better-auth version compatibility');
} Type guard
function hasUser(r: unknown): r is { user: User; token?: string | null } {
return typeof r === 'object' && r !== null && 'user' in r && typeof (r as { user: unknown }).user === 'object';
} Try / catch
try {
const result = await provider.signIn({ email, password });
if (!hasUser(result)) throw new Error('Sign-in succeeded but no user was returned');
} catch (e) {
if (e instanceof Error && e.message === 'Invalid email or password') {
console.error('Unexpected 2xx-without-user from better-auth; verify better-auth version');
}
throw e;
} Prevention
- Pin better-auth to a version within @mastra/auth-better-auth's supported range.
- Add a contract test asserting signIn() returns a user for valid credentials.
- Watch for proxies/interceptors that strip or reshape response bodies.
When it happens
Trigger: signIn({ email, password }) where response.ok === true but `result?.user` is falsy — e.g. body shape changed across better-auth versions, empty body, or a 200 with only a token/error field instead of a user.
Common situations: better-auth version drift where signInEmail's success payload shape differs from the expected { user, token }; proxy/CDN returning an empty 200; interceptors swallowing the response body.
Related errors
- Failed to create account
- ${errorData.message || 'Invalid email or password'}
- ${errorData.message || 'Failed to create account'}
- Better Auth instance is required. Please provide the auth op
- MastraAuthBetterAuth is not initialized — init() must run fi
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/f7454fe0ab9a371a.
Report an issue: GitHub.