> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Account conflict resolution

> The React SDK rendered the account-exists and merge screens for you. In the JavaScript SDK you catch the conflict errors and build the resolution UI yourself.

When a signed-in user adds a credential — another email, phone, social account, or wallet — that already belongs to a different account, a conflict arises. The React SDK detected this inside the auth-flow modal and rendered every screen (account-exists, merge, per-field conflicts, wallet-already-used) automatically. The JavaScript SDK surfaces the same cases as typed errors and hands the UI to you.

## What the React SDK did automatically

The widget pushed a built-in view for each conflict case. None of this was code you wrote — it happened inside the modal after a linking attempt.

| React SDK built-in screen     | What it handled                                                    |
| ----------------------------- | ------------------------------------------------------------------ |
| Account-exists prompt         | "This credential already belongs to an account — merge or switch?" |
| Merge confirmation            | Confirm the merge of the two accounts                              |
| Per-field conflict resolution | Resolve fields where the two accounts disagree                     |
| Same-email merge confirmation | Confirm a merge when both accounts share the same email            |
| Wallet-already-used notice    | "That wallet is already used by another account"                   |

Merge completion and closing the modal happened automatically once the user confirmed.

## What you own now

There is no modal and no automatic routing. The verification/linking call throws a typed error, and you branch on it to decide which UI to show.

| React SDK (automatic)         | JavaScript SDK (you call)                                                        |
| ----------------------------- | -------------------------------------------------------------------------------- |
| Account-exists / merge prompt | catch `MergeAccountsConfirmationError`, then render your confirm UI              |
| Per-field conflict resolution | read `error.mergeConflicts`, collect one `MergeUserConflictResolution` per field |
| Automatic merge completion    | `mergeUserAccounts({ conflictResolutions })` / `useMergeUserAccounts()`          |
| Wallet-already-used notice    | catch `WalletAlreadyLinkedToAnotherUserError`                                    |

## Errors you now own

The linking calls (`verifyOTP`, the social verification flow, `connectAndVerifyWithWalletProvider` / `verifyWalletAccount`) throw one of these when the credential belongs to someone else:

| Error                                   | Meaning                                                      | Recoverable                               |
| --------------------------------------- | ------------------------------------------------------------ | ----------------------------------------- |
| `MergeAccountsConfirmationError`        | The two accounts can be merged; carries `mergeConflicts`     | Yes — complete with `mergeUserAccounts()` |
| `WalletAlreadyLinkedToAnotherUserError` | The wallet is verified on another account and can't be moved | No                                        |
| `LinkCredentialError`                   | The credential can't be reassigned or merged                 | No                                        |

```typescript theme={"system"}
import {
  verifyWalletAccount,
  mergeUserAccounts,
  MergeAccountsConfirmationError,
  WalletAlreadyLinkedToAnotherUserError,
  LinkCredentialError,
} from '@dynamic-labs-sdk/client';

try {
  await verifyWalletAccount({ walletAccount });
} catch (error) {
  if (error instanceof MergeAccountsConfirmationError) {
    // resolveConflicts is your own UI: prompt the user to pick a value for
    // each conflicting field, returning MergeUserConflictResolution[].
    await mergeUserAccounts(
      error.mergeConflicts
        ? { conflictResolutions: resolveConflicts(error.mergeConflicts) }
        : undefined,
    );
  } else if (error instanceof WalletAlreadyLinkedToAnotherUserError) {
    showWalletAlreadyUsed();
  } else if (error instanceof LinkCredentialError) {
    showCannotReassign();
  } else {
    throw error;
  }
}
```

<Note>
  `mergeUserAccounts()` persists the merged session internally and returns the merged user — you don't re-authenticate afterward. Call it with no arguments when there are no conflicts, or with one resolution per conflicting field.
</Note>

## Completing the merge

Once the user has confirmed (and resolved any per-field conflicts), complete the merge. In React, `useMergeUserAccounts()` is a mutation whose resolved `data` is the merged user.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { mergeUserAccounts } from '@dynamic-labs-sdk/client';

    const mergedUser = await mergeUserAccounts({ conflictResolutions });
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import { useMergeUserAccounts } from '@dynamic-labs-sdk/react-hooks';

    const { mutateAsync: mergeAccounts, isPending } = useMergeUserAccounts();

    const mergedUser = await mergeAccounts({ conflictResolutions });
    ```
  </Tab>
</Tabs>

The full walkthrough — building the confirm dialog, the per-field conflict picker, and the wallet-already-used state — is in the Building UI guides.

## See also

* [Account conflict resolution (Building UI)](/docs/javascript/building-ui/account-conflict-resolution) — detect and route every conflict case
* [Account merging (Building UI)](/docs/javascript/building-ui/account-merging) — complete a merge end to end
* [Wallet authentication](/docs/javascript/migrating-from-react/wallet-authentication) — the wallet linking calls that surface these errors
* [Post-login user setup](/docs/javascript/migrating-from-react/post-login-user-setup)
