usememos/memos · error · Error

Failed to link account. Please sign in to Memos again and re

Error message

Failed to link account. Please sign in to Memos again and retry.

What it means

Thrown in AuthCallback's link flow when the OAuth provider redirected back with flowMode === "link" but currentUser?.name is falsy. Linking an identity requires an authenticated Memos session (the parent resource for createLinkedIdentity); if the session expired or tokens were lost during the round-trip to the IdP, there is no parent user to attach the identity to.

Source

Thrown at web/src/pages/AuthCallback.tsx:87

    // Validate OAuth state (CSRF protection) and retrieve PKCE code_verifier
    const validatedState = validateOAuthState(state);
    if (!validatedState) {
      setState({
        loading: false,
        errorMessage: "Failed to authorize. Invalid or expired state parameter. This may indicate a CSRF attack attempt.",
      });
      return;
    }

    const { flowMode, identityProviderName, returnUrl, linkingUserName, codeVerifier } = validatedState;
    const redirectUri = absolutifyLink("/auth/callback");
    handledRef.current = true;

    (async () => {
      try {
        if (flowMode === "link") {
          if (!currentUser?.name) {
            throw new Error("Failed to link account. Please sign in to Memos again and retry.");
          }
          if (linkingUserName && currentUser.name !== linkingUserName) {
            throw new Error("The signed-in user changed before the OAuth callback completed. Please retry linking from account settings.");
          }
          await userServiceClient.createLinkedIdentity({
            parent: currentUser.name,
            idpName: identityProviderName,
            code,
            redirectUri,
            codeVerifier: codeVerifier || "",
          });
        } else {
          const response = await authServiceClient.signIn({
            credentials: {
              case: "ssoCredentials",
              value: {
                idpName: identityProviderName,
                code,

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Sign in to Memos again in the same browser, then re-run the linking flow from account settings.
  2. Verify the Memos session survived: check web/src/auth-state.ts token storage and that the auth interceptor can refresh the access token.
  3. Ensure the OAuth redirect lands in the same browser profile/tab context where linking started (no 'open in new profile' links).
  4. If it recurs, inspect DevTools > Application for the auth tokens and confirm the IdP redirect_uri matches the origin.
Defensive patterns

Strategy: validation

Validate before calling

// before starting a link flow
if (!currentUser?.name) {
  showError("Sign in to Memos before linking an account.");
  return;
}
// embed the user into the state so the callback can verify
startLinkFlow(currentUser.name);

Type guard

const canLink = (user: { name?: string } | null | undefined): user is { name: string } =>
  typeof user?.name === "string" && user.name.length > 0;

Try / catch

catch (e) {
  if (e instanceof Error && e.message.includes("Failed to link account")) {
    await signOut(); // clear partial state
    navigate("/auth", { state: { reason: "relink" } });
  }
}

Prevention

When it happens

Trigger: User starts 'link account' from settings, gets redirected to the IdP, and meanwhile the Memos access/refresh token expires or auth-state storage is cleared; on callback, currentUser is null/undefined while stored state says flowMode === "link". Also occurs when the callback opens in a different browser profile or the auth BroadcastChannel state was reset.

Common situations: Long IdP login pages (password manager, 2FA) outlasting the Memos session; refresh token revoked; tokens stored per-tab and callback lands in a new tab; clock skew expiring the access token early.

Related errors


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/826edb772a63c082. Report an issue: GitHub.