[GH-ISSUE #6711] MongoDB Adapter: verifyApiKey fails with INVALID_API_KEY immediately after createApiKey #27921

Closed
opened 2026-04-17 19:12:46 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @balbuzar on GitHub (Dec 12, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6711

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Set up Better Auth with MongoDB adapter
  2. Create an API key using auth.api.createApiKey()
  3. Immediately verify the created key using auth.api.verifyApiKey()
  4. Observe the INVALID_API_KEY error

Minimal reproduction code:

const createApiKeyData = await auth.api.createApiKey({
    body: {
        name: 'project-api-key',
        expiresIn: 60 * 60 * 24 * 7,
        userId: "user-id",
    },
});

const data = await auth.api.verifyApiKey({
    body: {
        key: createApiKeyData.key,
    },
});

Current vs. Expected behavior

The verifyApiKey method throws an INVALID_API_KEY error immediately after creating an API key. This error is misleading as it's actually a fallback error when the underlying database update operation fails silently.

After adding a try-catch block around the adapter update operation at verify-api-key.ts#L187, the actual error is revealed:

codeName: "ImmutableField",
[...]
Plan executor error during findAndModify :: caused by :: Performing an update on the path '_id' would modify the immutable field '_id'

The issue occurs because the updated object contains an _id field (MongoDB's immutable identifier), which MongoDB refuses to update.

Expected behavior:

The verifyApiKey should successfully verify a newly created API key without errors. The adapter should either:

  • Not include the immutable _id field in the update operation
  • Properly handle and surface the actual database error instead of returning a generic INVALID_API_KEY error

Proposed Solution

A temporary fix is to remove the id field from the updated object before performing the update:

delete updated.id;

newApiKey = await ctx.context.adapter.update<ApiKey>({
    model: API_KEY_TABLE_NAME,
    where: [
        {
            field: "id",
            value: apiKey.id,
        },
    ],
    update: updated,
});

However, this needs to be validated to ensure it doesn't break compatibility with other storage adapters.

What version of Better Auth are you using?

1.4.6

System info

{
  "system": {
    "platform": "win32",
    "arch": "x64",
    "version": "Windows 11 Pro",
    "release": "10.0.22631",
    "cpuCount": 24,
    "cpuModel": "13th Gen Intel(R) Core(TM) i7-13700KF",
    "totalMemory": "31.84 GB",
    "freeMemory": "1.55 GB"
  },
  "node": {
    "version": "v22.16.0",
    "env": "development"
  },
  "packageManager": {
    "name": "bun",
    "version": "1.3.4"
  },
  "frameworks": [
    {
      "name": "hono",
      "version": "4.10.8"
    }
  ],
  "databases": null,
  "betterAuth": {
    "version": "1.4.6",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
  database: mongodbAdapter(client.db(), {
      client: client,
  })
});

Additional context

  • I cant affirm that this issue is specific to the MongoDB adapter
  • The root cause is in the verify API key route where the adapter update is called
  • The current error handling masks the real issue by returning a generic INVALID_API_KEY error
Originally created by @balbuzar on GitHub (Dec 12, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6711 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Set up Better Auth with MongoDB adapter 2. Create an API key using `auth.api.createApiKey()` 3. Immediately verify the created key using `auth.api.verifyApiKey()` 4. Observe the `INVALID_API_KEY` error **Minimal reproduction code:** ```typescript const createApiKeyData = await auth.api.createApiKey({ body: { name: 'project-api-key', expiresIn: 60 * 60 * 24 * 7, userId: "user-id", }, }); const data = await auth.api.verifyApiKey({ body: { key: createApiKeyData.key, }, }); ``` ### Current vs. Expected behavior The `verifyApiKey` method throws an `INVALID_API_KEY` error immediately after creating an API key. This error is misleading as it's actually a fallback error when the underlying database update operation fails silently. After adding a try-catch block around the adapter update operation at [verify-api-key.ts#L187](https://github.com/better-auth/better-auth/blob/v1.4.7-beta.3/packages/better-auth/src/plugins/api-key/routes/verify-api-key.ts#L187), the actual error is revealed: ``` codeName: "ImmutableField", [...] Plan executor error during findAndModify :: caused by :: Performing an update on the path '_id' would modify the immutable field '_id' ``` The issue occurs because the `updated` object contains an `_id` field (MongoDB's immutable identifier), which MongoDB refuses to update. #### Expected behavior: The `verifyApiKey` should successfully verify a newly created API key without errors. The adapter should either: - Not include the immutable `_id` field in the update operation - Properly handle and surface the actual database error instead of returning a generic `INVALID_API_KEY` error #### Proposed Solution A temporary fix is to remove the `id` field from the `updated` object before performing the update: ```typescript delete updated.id; newApiKey = await ctx.context.adapter.update<ApiKey>({ model: API_KEY_TABLE_NAME, where: [ { field: "id", value: apiKey.id, }, ], update: updated, }); ``` However, this needs to be validated to ensure it doesn't break compatibility with other storage adapters. ### What version of Better Auth are you using? 1.4.6 ### System info ```bash { "system": { "platform": "win32", "arch": "x64", "version": "Windows 11 Pro", "release": "10.0.22631", "cpuCount": 24, "cpuModel": "13th Gen Intel(R) Core(TM) i7-13700KF", "totalMemory": "31.84 GB", "freeMemory": "1.55 GB" }, "node": { "version": "v22.16.0", "env": "development" }, "packageManager": { "name": "bun", "version": "1.3.4" }, "frameworks": [ { "name": "hono", "version": "4.10.8" } ], "databases": null, "betterAuth": { "version": "1.4.6", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, database: mongodbAdapter(client.db(), { client: client, }) }); ``` ### Additional context - I cant affirm that this issue is specific to the MongoDB adapter - The root cause is in the verify API key route where the adapter update is called - The current error handling masks the real issue by returning a generic `INVALID_API_KEY` error
GiteaMirror added the locked label 2026-04-17 19:12:46 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Dec 12, 2025):

Hi @balbuzar! I'm Dosu and I’m helping the better-auth team.

This is a confirmed issue with the MongoDB adapter: when you call verifyApiKey, the update payload includes the id field, which the adapter maps to MongoDB’s immutable _id field. MongoDB rejects any attempt to update _id, causing the operation to fail. However, the error is masked by the current error handling, which returns a generic INVALID_API_KEY error instead of surfacing the real database error [source].

Removing the id field from the update object before calling the adapter’s update method is a safe workaround. Other adapters (memory, Prisma, Drizzle, Kysely) do not treat id as immutable and rely on the where clause to identify the record, so stripping id from the update payload will not break compatibility [source].

For a robust fix: (1) update the verifyApiKey route to remove id (and/or _id) from the update payload before calling the adapter, and (2) improve error handling so that database errors are surfaced instead of being masked by a generic error. There’s currently no framework-level utility for handling immutable fields like this, so documenting this behavior or adding a utility would help prevent similar issues in the future [source].

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3645981888 --> @dosubot[bot] commented on GitHub (Dec 12, 2025): <!-- Greeting --> Hi @balbuzar! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a confirmed issue with the MongoDB adapter: when you call `verifyApiKey`, the update payload includes the `id` field, which the adapter maps to MongoDB’s immutable `_id` field. MongoDB rejects any attempt to update `_id`, causing the operation to fail. However, the error is masked by the current error handling, which returns a generic `INVALID_API_KEY` error instead of surfacing the real database error [[source](https://github.com/better-auth/better-auth/blob/6089a57dc7692f26cceaf1f26611bc5ff99f66d3/packages/better-auth/src/plugins/api-key/routes/verify-api-key.ts#L15-L331)]. Removing the `id` field from the update object before calling the adapter’s update method is a safe workaround. Other adapters (memory, Prisma, Drizzle, Kysely) do not treat `id` as immutable and rely on the `where` clause to identify the record, so stripping `id` from the update payload will not break compatibility [[source](https://github.com/better-auth/better-auth/blob/6089a57dc7692f26cceaf1f26611bc5ff99f66d3/packages/better-auth/src/adapters/prisma-adapter/prisma-adapter.ts#L352-L363)]. For a robust fix: (1) update the verifyApiKey route to remove `id` (and/or `_id`) from the update payload before calling the adapter, and (2) improve error handling so that database errors are surfaced instead of being masked by a generic error. There’s currently no framework-level utility for handling immutable fields like this, so documenting this behavior or adding a utility would help prevent similar issues in the future [[source](https://github.com/better-auth/better-auth/blob/6089a57dc7692f26cceaf1f26611bc5ff99f66d3/packages/better-auth/src/adapters/mongodb-adapter/mongodb-adapter.ts#L505-L659)]. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/dd256837-8982-4c2c-983d-64cbeb793aa8?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/dd256837-8982-4c2c-983d-64cbeb793aa8?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/dd256837-8982-4c2c-983d-64cbeb793aa8?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/dd256837-8982-4c2c-983d-64cbeb793aa8?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/dd256837-8982-4c2c-983d-64cbeb793aa8?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/dd256837-8982-4c2c-983d-64cbeb793aa8?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/dd256837-8982-4c2c-983d-64cbeb793aa8?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20is%20a%20confirmed%20issue%20with%20the%20MongoDB%20adapter%3A%20when%20you%20call%20%60verifyApiKey%60%2C%20the%20update%20payload%20includes%20the%20%60id%60%20field%2C%20which%20the%20adapter%20maps%20to%20MongoDB%E2%80%99s%20immutable%20%60_id%60%20field.%20MongoDB%20rejects%20any%20attempt%20to%20update%20%60_id%60%2C%20causing%20the%20operation%20to%20fail.%20However%2C%20the%20error%20is%20masked%20by%20the%20current%20error%20handling%2C%20which%20returns%20a%20generic%20%60INVALID_API_KEY%60%20error%20instead%20of%20surfacing%20the%20real%20database%20error%20%5B%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/blob/6089a57dc7692f26cceaf1f26611bc5ff99f66d3/packages/better-auth/src/plugins/api-key/routes/verify-api-key.ts%23L15-L331%29%5D.%0A%0ARemoving%20the%20%60id%60%20field%20from%20the%20update%20object%20before%20calling%20the%20adapter%E2%80%99s%20update%20method%20is%20a%20safe%20workaround.%20Other%20adapters%20%28memory%2C%20Prisma%2C%20Drizzle%2C%20Kysely%29%20do%20not%20treat%20%60id%60%20as%20immutable%20and%20rely%20on%20the%20%60where%60%20clause%20to%20identify%20the%20record%2C%20so%20stripping%20%60id%60%20from%20the%20update%20payload%20will%20not%20break%20compatibility%20%5B%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/blob/6089a57dc7692f26cceaf1f26611bc5ff99f66d3/packages/better-auth/src/adapters/prisma-adapter/prisma-adapter.ts%23L352-L363%29%5D.%0A%0AFor%20a%20robust%20fix%3A%20%281%29%20update%20the%20verifyApiKey%20route%20to%20remove%20%60id%60%20%28and/or%20%60_id%60%29%20from%20the%20update%20payload%20before%20calling%20the%20adapter%2C%20and%20%282%29%20improve%20error%20handling%20so%20that%20database%20errors%20are%20surfaced%20instead%20of%20being%20masked%20by%20a%20generic%20error.%20There%E2%80%99s%20currently%20no%20framework-level%20utility%20for%20handling%20immutable%20fields%20like%20this%2C%20so%20documenting%20this%20behavior%20or%20adding%20a%20utility%20would%20help%20prevent%20similar%20issues%20in%20the%20future%20%5B%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/blob/6089a57dc7692f26cceaf1f26611bc5ff99f66d3/packages/better-auth/src/adapters/mongodb-adapter/mongodb-adapter.ts%23L505-L659%29%5D.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/6711)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#27921