moeru-ai/airi · error · Error

unlinkAccount failed

Error message

unlinkAccount failed

What it means

Fallback message when client.unlinkAccount({ providerId }) returns an error result with no message of its own. The composable's guard already blocks unlinking the last sign-in method, so reaching this throw means the server rejected the call itself — most often because the account row no longer exists server-side (stale UI list) or the session expired. The catch sets error.value for display.

Solutions

  1. Call refresh() and retry the unlink once — a stale list is the most common cause
  2. Re-authenticate if the session expired, then unlink again
  3. Verify the providerId being sent matches a row currently present in linkedAccounts
  4. Check auth server logs for the unlink route if the failure persists

Example fix

// before
await unlink(providerId) // 'unlinkAccount failed' because the list went stale

// after
if (!linkedAccounts.value.some(a => a.providerId === providerId)) {
  await refresh()
  return
}
await unlink(providerId)
Defensive patterns

Strategy: try-catch

Validate before calling

const stillLinked = linkedAccounts.value.some(a => a.providerId === providerId)
if (!stillLinked) {
  await refresh() // resync before hitting the server
}

Try / catch

try {
  await unlink(providerId)
}
catch {
  // error.value is already set by the composable; offer a retry that
  // refreshes first, since stale lists are the dominant cause.
  showRetryDialog(() => { refresh(); unlink(providerId) })
}

Prevention

When it happens

Trigger: Unlinking a provider that was already removed (list went stale after unlinking in another tab or after re-login elsewhere); expired session cookie on the unlink mutation; a better-auth server error while deleting the provider record.

Common situations: Two settings tabs open and one already unlinked; user re-linked the provider from another device; auth service restarted mid-session.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/729f7036ded19644. Report an issue: GitHub.

Appendix: source

Thrown at packages/stage-ui/src/composables/use-linked-accounts.ts:205

  async function unlink(providerId: string, providerName: string) {
    if (inFlight.value)
      return

    if (isLastSignInMethod(providerId)) {
      error.value = args.messages.lastAccount
      message.value = null
      return
    }

    inFlight.value = providerId
    error.value = null
    message.value = null

    try {
      const { error: apiError } = await args.client.unlinkAccount({ providerId })
      if (apiError)
        throw new Error(apiError.message ?? 'unlinkAccount failed')
      message.value = args.messages.unlinked(providerName)
      args.onUnlinked?.(providerId)
      await refresh()
    }
    catch (err) {
      error.value = args.describeError(err) || args.messages.unlinkFailed
    }
    finally {
      inFlight.value = null
    }
  }

  async function link(providerId: LinkedProviderId, providerName: string) {
    if (inFlight.value)
      return

    inFlight.value = providerId
    error.value = null

View on GitHub (pinned to 677329427f)