mastra-ai/mastra · error

${errorData.message || 'Failed to create account'}

Error message

${errorData.message || 'Failed to create account'}

What it means

signUp() calls Better Auth's sign-up endpoint (auth.api.signUpEmail with asResponse: true). On a non-OK response it parses the JSON body for a `message` and throws it, defaulting to 'Failed to create account'. The message usually carries Better Auth's reason: user already exists, weak password, validation failure.

Source

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

  async signUp(
    email: string,
    password: string,
    name: string | undefined,
    request: Request,
  ): Promise<CredentialsResult<EEUser>> {
    const displayName = name ?? email.split('@')[0] ?? 'User';
    const headers = request?.headers ?? new Headers();

    // Use asResponse: true to get the full response with Set-Cookie headers
    const response = await this.auth.api.signUpEmail({
      body: { email, password, name: displayName },
      headers,
      asResponse: true,
    });

    if (!response.ok) {
      const errorData = (await response.json().catch(() => ({}))) as { message?: string };
      throw new Error(errorData.message || 'Failed to create account');
    }

    const result = (await response.json()) as { user?: User; token?: string | null };

    if (!result?.user) {
      throw new Error('Failed to create account');
    }

    // 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. Read errorData.message to identify the concrete cause — for duplicates, sign in instead or use a unique email.
  2. Ensure the password meets the configured policy (better-auth default minPasswordLength: 8).
  3. Verify the auth database is migrated (users/account tables exist) and writable.
  4. For tests, reset or truncate auth tables between runs to avoid duplicate-email conflicts.

Example fix

// before: password shorter than better-auth default (8 chars)
await auth.signUp({ email: 'new@example.com', password: 'short', name: 'X' });

// after
await auth.signUp({ email: 'new@example.com', password: 'hunter2secure', name: 'X' });
Defensive patterns

Strategy: try-catch

Validate before calling

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

Type guard

function isSignUpRejection(e: unknown): e is Error {
  return e instanceof Error && (e.message === 'Failed to create account' || /already exists|password/i.test(e.message));
}

Try / catch

try {
  await provider.signUp({ email, password, name });
} catch (e) {
  if (isSignUpRejection(e)) {
    if (/already exists/i.test(e.message)) return { ok: false, reason: 'email-taken' };
    if (/password/i.test(e.message)) return { ok: false, reason: 'weak-password' };
    return { ok: false, reason: 'signup-failed', detail: e.message };
  }
  throw e;
}

Prevention

When it happens

Trigger: signUp({ email, password, name }) where auth.api.signUpEmail returns response.ok === false — duplicate email (USER_ALREADY_EXISTS), password below min length policy, invalid email format, or database write failure; errorData.message replaces the default when present.

Common situations: Re-registering an existing account during tests without cleanup; passwords that violate better-auth's password config (minPasswordLength); sign-up attempted before the users table/migration exists in the auth database.

Related errors


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