Significant-Gravitas/AutoGPT · error · Error

Failed to update email

Error message

Failed to update email

What it means

Fallback message from requestEmailChange in the profile settings EmailForm: it PUTs {email} to the Next.js route /api/auth/user and, on a non-ok response, tries to surface error.error from the JSON body, falling back to 'Failed to update email' when the body has no error field or fails to parse. Note: unlike its sibling in useAccountCard (error 30), this version does NOT .catch() the response.json() call — a non-JSON error body throws a SyntaxError here instead, shadowing this message.

Source

Thrown at autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/components/EmailForm/useEmailForm.ts:41

// Better Auth owns the email change. For a verified user it emails a
// confirmation link to their CURRENT address and only applies the new email
// once that link is clicked (anti-takeover); for an unverified user the change
// applies immediately. Platform User.email (notifications / Stripe) then
// converges via the databaseHooks.user.update mirror in lib/auth/auth.ts — we
// deliberately do NOT write the platform email here, so it can never diverge to
// an unverified value.
async function requestEmailChange(email: string) {
  const response = await fetch("/api/auth/user", {
    method: "PUT",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ email }),
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error || "Failed to update email");
  }

  return response.json();
}

export function useEmailForm({ user }: { user: User }) {
  const { toast } = useToast();
  const defaultValues = createEmailDefaultValues(user);
  const currentEmail = user.email;
  const [isSubmitting, setIsSubmitting] = useState(false);

  const form = useForm<z.infer<typeof emailFormSchema>>({
    resolver: zodResolver(emailFormSchema),
    defaultValues,
    mode: "onSubmit",
  });

  async function onSubmit(values: z.infer<typeof emailFormSchema>) {

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Check the PUT /api/auth/user response in DevTools — the Supabase error string (error.error) is usually shown; the generic message means the body was empty/HTML.
  2. If 401, sign in again before retrying.
  3. If 429, wait — Supabase limits email-change frequency.
  4. Harden requestEmailChange with response.json().catch(() => ({})) as its sibling useAccountCard already does, so non-JSON bodies produce this message instead of a SyntaxError.

Example fix

// before
const error = await response.json();
throw new Error(error.error || "Failed to update email");

// after
const error = await response.json().catch(() => ({}));
throw new Error(error.error || "Failed to update email");
Defensive patterns

Strategy: try-catch

Validate before calling

const emailSchema = z.string().email();
const parsed = emailSchema.safeParse(email);
if (!parsed.success) { /* show field error, skip submit */ }

Type guard

function isEmailUpdateFailure(err: unknown): boolean {
  return err instanceof Error && err.message === "Failed to update email";
}

Try / catch

try {
  await requestEmailChange(email);
} catch (error) {
  // error.message is usually the Supabase error string; generic fallback
  toast({ description: (error as Error).message, variant: "destructive" });
}

Prevention

When it happens

Trigger: PUT /api/auth/user returning 400 (email already in use, invalid format), 401 (session expired), 429 (Supabase rate limit on email changes), or a non-JSON body (HTML 500 page from the route) — the last case throws a JSON parse error rather than this message.

Common situations: Changing email to one already registered; Supabase auth rate-limiting repeated change attempts; expired session in a long-open settings tab; dev misconfiguration of the auth route.

Related errors


AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14). Data as JSON: /api/errors/ad54063956b853f9. Report an issue: GitHub.