[GH-ISSUE #8819] Secondary storage (Redis) caches session without database-generated id field #11200

Closed
opened 2026-04-13 07:32:58 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @juliuslipp on GitHub (Mar 28, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8819

Description

When using secondary storage (e.g. Redis) with advanced.database.generateId: false (relying on the database to generate IDs), the session object cached in secondary storage is missing the id field.

Root Cause

In internal-adapter.mjs, createSession stores the session data in secondary storage before the database insert:

  1. Session data object is built (no id — it's generated by the DB, e.g. Postgres uuidv7())
  2. createWithHooks(data, "session", customCreateFn) is called
  3. customCreateFn.fn(actualData) stores the session in Redis — pre-insert, no id
  4. The DB adapter's create() runs and Postgres generates the id
  5. The DB returns the full row (with id), but Redis already has the id-less version

When getSession later reads from Redis (or cookie cache populated from Redis), session.id is undefined.

Reproduction

  1. Configure better-auth with:
    • advanced.database.generateId: false
    • Secondary storage (Redis)
    • Database with auto-generated UUIDs (e.g. Postgres DEFAULT uuidv7())
  2. Create a session (sign in)
  3. Call auth.api.getSession() — the returned session.id is undefined

All other fields (token, userId, expiresAt, createdAt, etc.) are present because they're set in JS before the insert. Only id is missing.

Expected Behavior

session.id should always be present in the object returned by getSession(), regardless of whether it's served from the database or secondary storage.

Suggested Fix

Store the post-insert data (which includes the DB-generated id) in secondary storage instead of the pre-insert data. Alternatively, the secondary storage write could happen after the DB insert completes.

Originally created by @juliuslipp on GitHub (Mar 28, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8819 ## Description When using secondary storage (e.g. Redis) with `advanced.database.generateId: false` (relying on the database to generate IDs), the session object cached in secondary storage is missing the `id` field. ## Root Cause In `internal-adapter.mjs`, `createSession` stores the session data in secondary storage **before** the database insert: 1. Session `data` object is built (no `id` — it's generated by the DB, e.g. Postgres `uuidv7()`) 2. `createWithHooks(data, "session", customCreateFn)` is called 3. `customCreateFn.fn(actualData)` stores the session in Redis — **pre-insert, no `id`** 4. The DB adapter's `create()` runs and Postgres generates the `id` 5. The DB returns the full row (with `id`), but Redis already has the `id`-less version When `getSession` later reads from Redis (or cookie cache populated from Redis), `session.id` is `undefined`. ## Reproduction 1. Configure better-auth with: - `advanced.database.generateId: false` - Secondary storage (Redis) - Database with auto-generated UUIDs (e.g. Postgres `DEFAULT uuidv7()`) 2. Create a session (sign in) 3. Call `auth.api.getSession()` — the returned `session.id` is `undefined` All other fields (`token`, `userId`, `expiresAt`, `createdAt`, etc.) are present because they're set in JS before the insert. Only `id` is missing. ## Expected Behavior `session.id` should always be present in the object returned by `getSession()`, regardless of whether it's served from the database or secondary storage. ## Suggested Fix Store the **post-insert** data (which includes the DB-generated `id`) in secondary storage instead of the pre-insert data. Alternatively, the secondary storage write could happen after the DB insert completes.
GiteaMirror added the lockedbug labels 2026-04-13 07:32:58 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Mar 28, 2026):

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

This is a confirmed bug that's been reported in #6993 and #7454. PR #8101 proposes a fix but remains unmerged.

Your analysis of the root cause is accurate — secondary storage receives the session object before the database generates the id.

Workaround: Use databaseHooks.session.create.after to patch Redis after the DB insert:

databaseHooks: {
  session: {
    create: {
      after: async (session) => {
        if (redisClient && session.id && session.token) {
          const cached = await redisClient.get(session.token);
          if (cached) {
            const parsed = JSON.parse(cached);
            if (parsed?.session && !parsed.session.id) {
              parsed.session.id = session.id;
              const ttl = await redisClient.ttl(session.token);
              await redisClient.set(
                session.token,
                JSON.stringify(parsed),
                ttl > 0 ? { EX: ttl } : undefined,
              );
            }
          }
        }
      },
    },
  },
}

This hook fires after the database insert completes (with the generated id), retrieves the cached session from Redis, and patches in the missing id while preserving the TTL.

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:4148827474 --> @dosubot[bot] commented on GitHub (Mar 28, 2026): <!-- Greeting --> Hi @juliuslipp! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a **confirmed bug** that's been reported in [#6993](https://github.com/better-auth/better-auth/issues/6993) and [#7454](https://github.com/better-auth/better-auth/issues/7454). [PR #8101](https://github.com/better-auth/better-auth/pull/8101) proposes a fix but remains unmerged. Your analysis of the root cause is accurate — secondary storage receives the session object before the database generates the `id`. **Workaround:** Use `databaseHooks.session.create.after` to patch Redis after the DB insert: ```typescript databaseHooks: { session: { create: { after: async (session) => { if (redisClient && session.id && session.token) { const cached = await redisClient.get(session.token); if (cached) { const parsed = JSON.parse(cached); if (parsed?.session && !parsed.session.id) { parsed.session.id = session.id; const ttl = await redisClient.ttl(session.token); await redisClient.set( session.token, JSON.stringify(parsed), ttl > 0 ? { EX: ttl } : undefined, ); } } } }, }, }, } ``` This hook fires after the database insert completes (with the generated `id`), retrieves the cached session from Redis, and patches in the missing `id` while preserving the TTL. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=brand-link).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/3acfdde3-3103-4f2b-839e-5a7665d64586?feedback_type=great_response&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/3acfdde3-3103-4f2b-839e-5a7665d64586?feedback_type=irrelevant_answer&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/3acfdde3-3103-4f2b-839e-5a7665d64586?feedback_type=incorrect_sources&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/3acfdde3-3103-4f2b-839e-5a7665d64586?feedback_type=too_verbose&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/3acfdde3-3103-4f2b-839e-5a7665d64586?feedback_type=hallucination&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/3acfdde3-3103-4f2b-839e-5a7665d64586?feedback_type=bug_report&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report) | [Other](https://app.dosu.dev/response-feedback/3acfdde3-3103-4f2b-839e-5a7665d64586?feedback_type=other&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-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&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=chat-badge)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20is%20a%20%2A%2Aconfirmed%20bug%2A%2A%20that%27s%20been%20reported%20in%20%5B%236993%5D%28https%3A//github.com/better-auth/better-auth/issues/6993%29%20and%20%5B%237454%5D%28https%3A//github.com/better-auth/better-auth/issues/7454%29.%20%5BPR%20%238101%5D%28https%3A//github.com/better-auth/better-auth/pull/8101%29%20proposes%20a%20fix%20but%20remains%20unmerged.%0A%0AYour%20analysis%20of%20the%20root%20cause%20is%20accurate%20%E2%80%94%20secondary%20storage%20receives%20the%20session%20object%20before%20the%20database%20generates%20the%20%60id%60.%0A%0A%2A%2AWorkaround%3A%2A%2A%20Use%20%60databaseHooks.session.create.after%60%20to%20patch%20Redis%20after%20the%20DB%20insert%3A%0A%0A%60%60%60typescript%0AdatabaseHooks%3A%20%7B%0A%20%20session%3A%20%7B%0A%20%20%20%20create%3A%20%7B%0A%20%20%20%20%20%20after%3A%20async%20%28session%29%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20if%20%28redisClient%20%26%26%20session.id%20%26%26%20session.token%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20const%20cached%20%3D%20await%20redisClient.get%28session.token%29%3B%0A%20%20%20%20%20%20%20%20%20%20if%20%28cached%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20parsed%20%3D%20JSON.parse%28cached%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28parsed%3F.session%20%26%26%20%21parsed.session.id%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20parsed.session.id%20%3D%20session.id%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20ttl%20%3D%20await%20redisClient.ttl%28session.token%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20await%20redisClient.set%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20session.token%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20JSON.stringify%28parsed%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ttl%20%3E%200%20%3F%20%7B%20EX%3A%20ttl%20%7D%20%3A%20undefined%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%7D%2C%0A%20%20%7D%2C%0A%7D%0A%60%60%60%0A%0AThis%20hook%20fires%20after%20the%20database%20insert%20completes%20%28with%20the%20generated%20%60id%60%29%2C%20retrieves%20the%20cached%20session%20from%20Redis%2C%20and%20patches%20in%20the%20missing%20%60id%60%20while%20preserving%20the%20TTL.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=join-discord)&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/8819)
Author
Owner

@juliuslipp commented on GitHub (Mar 28, 2026):

Closing — this is a duplicate of #6993 and #7454. PR #8101 has a proposed fix. Thanks @dosu for the pointer!

<!-- gh-comment-id:4148830248 --> @juliuslipp commented on GitHub (Mar 28, 2026): Closing — this is a duplicate of #6993 and #7454. PR #8101 has a proposed fix. Thanks @dosu for the pointer!
Author
Owner

@dosubot[bot] commented on GitHub (Mar 28, 2026):

Happy to help! Good luck with the workaround in the meantime. 👍

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:4148830730 --> @dosubot[bot] commented on GitHub (Mar 28, 2026): <!-- Answer --> Happy to help! Good luck with the workaround in the meantime. 👍 <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=brand-link).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/0d729d46-8c57-4eee-8a7f-1db41dcd0297?feedback_type=great_response&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/0d729d46-8c57-4eee-8a7f-1db41dcd0297?feedback_type=irrelevant_answer&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/0d729d46-8c57-4eee-8a7f-1db41dcd0297?feedback_type=incorrect_sources&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/0d729d46-8c57-4eee-8a7f-1db41dcd0297?feedback_type=too_verbose&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/0d729d46-8c57-4eee-8a7f-1db41dcd0297?feedback_type=hallucination&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/0d729d46-8c57-4eee-8a7f-1db41dcd0297?feedback_type=bug_report&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report) | [Other](https://app.dosu.dev/response-feedback/0d729d46-8c57-4eee-8a7f-1db41dcd0297?feedback_type=other&utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-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&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=chat-badge)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=Happy%20to%20help%21%20Good%20luck%20with%20the%20workaround%20in%20the%20meantime.%20%F0%9F%91%8D)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=join-discord)&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/8819)
Author
Owner

@github-actions[bot] commented on GitHub (Apr 5, 2026):

This issue has been locked as it was closed more than 7 days ago. If you're experiencing a similar problem or you have additional context, please open a new issue and reference this one.

<!-- gh-comment-id:4187977687 --> @github-actions[bot] commented on GitHub (Apr 5, 2026): This issue has been locked as it was closed more than 7 days ago. If you're experiencing a similar problem or you have additional context, please open a new issue and reference this one.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#11200