mastra-ai/mastra · error

${errorData.message || 'Invalid email or password'}

Error message

${errorData.message || 'Invalid email or password'}

What it means

signIn() calls Better Auth's sign-in endpoint (via auth.api.signInEmail with asResponse: true). If the HTTP response is not OK, it tries to parse a JSON body for a `message` and throws that message, defaulting to 'Invalid email or password'. This is the provider surfacing the server-side rejection of the credential pair.

Source

Thrown at auth/better-auth/src/index.ts:759

   * @param email - User email
   * @param password - User password
   * @param request - Incoming HTTP request
   * @returns Result with user and session cookies
   * @throws Error if credentials are invalid
   */
  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),

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Verify the email/password combination is correct and the user exists in the Better Auth database the server is connected to.
  2. Surface the underlying errorData.message from the response body to pinpoint the actual server rejection (rate limit, disabled account, etc.).
  3. If the user is OAuth-only, use the appropriate OAuth sign-in flow instead of email/password.
  4. Confirm the server instance (auth option) points at the same database used at sign-up.

Example fix

// before: signing in against a fresh test DB with no such user
await auth.signIn({ email: 'dev@example.com', password: 'hunter2' }); // throws

// after: sign up first (or use seeded fixtures)
await auth.signUp({ email: 'dev@example.com', password: 'hunter2', name: 'Dev' });
await auth.signIn({ email: 'dev@example.com', password: 'hunter2' });
Defensive patterns

Strategy: try-catch

Validate before calling

function isValidSignInInput(input: { email: string; password: string }): boolean {
  return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(input.email) && input.password.length > 0;
}

Type guard

function isInvalidCredentialsError(e: unknown): e is Error {
  return e instanceof Error && (e.message === 'Invalid email or password' || /invalid|credential/i.test(e.message));
}

Try / catch

try {
  await provider.signIn({ email, password });
} catch (e) {
  if (isInvalidCredentialsError(e)) {
    return { ok: false, reason: 'bad-credentials' }; // do not leak which field was wrong
  }
  throw e;
}

Prevention

When it happens

Trigger: signIn({ email, password }) where auth.api.signInEmail returns response.ok === false — unknown email, wrong password, account without a password credential (OAuth-only), rate-limited or disabled account; errorData.message is used when the error body contains one.

Common situations: User typos credentials; user registered via social/OAuth so no password exists; test fixtures using a user never signed up in the test database; server misconfigured against a different database than where the user registered.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/c9a40cfaedb791a3. Report an issue: GitHub.