nextauthjs/next-auth · error
Account not found
Error message
Account not found
What it means
unlinkAccount looks up the AccountModel by the composite { provider, providerAccountId } and throws 'Account not found' if no row matches, because there is nothing to remove. This adapter requires the account to exist; some other adapters just resolve silently, so callers expecting a no-op will see this error.
Source
Thrown at packages/adapter-mikro-orm/src/index.ts:159
// @ts-expect-error
async linkAccount(data) {
const em = await getEM()
const user = await em.findOne(UserModel, { id: data.userId })
if (!user) throw new Error("User not found")
const account = new AccountModel()
wrap(account).assign(data as object)
user.accounts.add(account)
await em.persistAndFlush(user)
return wrap(account).toObject()
},
// @ts-expect-error
async unlinkAccount(provider_providerAccountId) {
const em = await getEM()
const account = await em.findOne(AccountModel, {
...provider_providerAccountId,
})
if (!account) throw new Error("Account not found")
await em.removeAndFlush(account)
return wrap(account).toObject()
},
async getSessionAndUser(sessionToken) {
const em = await getEM()
const session = await em.findOne(
SessionModel,
{ sessionToken },
{ populate: ["user"] }
)
if (!session || !session.user) return null
return {
user: wrap(session.user).toObject(),
session: wrap(session).toObject(),
}
},View on GitHub (pinned to a1a16a5a77)
Solutions
- Confirm the account row exists for that exact provider + providerAccountId pair before unlinking
- Guard with a lookup (getSessionAndUser/getUser) or catch the error and treat as already-unlinked
- Deduplicate the unlink action in UI/API to avoid double deletion
- Check that provider and providerAccountId values match what was stored at link time (casing, ids)
Example fix
// before
await adapter.unlinkAccount({ provider: 'github', providerAccountId })
// after
try {
await adapter.unlinkAccount({ provider: 'github', providerAccountId })
} catch (e) {
if (!(e as Error).message.includes('Account not found')) throw e
} Defensive patterns
Strategy: try-catch
Validate before calling
const user = await adapter.getUserByAccount({ provider, providerAccountId })
if (!user) return // already unlinked
await adapter.unlinkAccount({ provider, providerAccountId }) Type guard
function hasCompositeKey(v: unknown): v is { provider: string; providerAccountId: string } {
return typeof v === 'object' && v !== null &&
typeof (v as any).provider === 'string' && typeof (v as any).providerAccountId === 'string'
} Try / catch
try {
await adapter.unlinkAccount({ provider, providerAccountId })
} catch (e) {
if ((e as Error).message === 'Account not found') {
return // idempotent: treat as already removed
} else throw e
} Prevention
- Make unlink idempotent in caller code
- Check getUserByAccount before unlinking
- Deduplicate unlink buttons/actions
- Verify provider/providerAccountId values match stored data
When it happens
Trigger: Calling unlinkAccount with a provider/providerAccountId pair that was never stored, after the account row was already deleted, or with swapped/mismatched key names (e.g., passing providerAccountId where provider is expected).
Common situations: Double-unlink from duplicated UI actions; account removed by a cleanup job but still referenced in the client; wrong composite keys after renaming fields; manual adapter calls with data shaped differently than the DB.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- User not found
- Session not found
- [updateSession] Failed to fetch updated session
- Object is nullish
- User not created
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/52965504585c2d1b.
Report an issue: GitHub.