[GH-ISSUE #9151] Expo stateless: auth.api.accountInfo/getAccessToken return ACCOUNT_NOT_FOUND after sign-in in 1.6.2, worked in 1.4.17 #28612

Open
opened 2026-04-17 20:02:57 -05:00 by GiteaMirror · 12 comments
Owner

Originally created by @ruff-exec on GitHub (Apr 13, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/9151

Is this suited for github?

  • Yes, this is suited for github

Reproduction

  1. Start from an Expo app using Better Auth with:
    • expo()
    • genericOAuth + keycloak
    • customSession(...)
    • stateless cookie setup:
      • storeStateStrategy: "cookie"
      • storeAccountCookie: true
  2. Inside customSession(...), call:
    • auth.api.accountInfo({ headers: ctx.headers })
    • auth.api.getAccessToken({ body: { providerId: "keycloak" }, headers: ctx.headers })
  3. Sign in through Keycloak in the Expo app.
  4. Return to the app after successful OAuth redirect.
  5. Observe that sign-in itself succeeds, but session enrichment fails and useSession() remains null.

This worked for us before upgrading from:

  • better-auth@1.4.17
  • @better-auth/expo@1.4.17

It now fails on:

  • better-auth@1.6.2
  • @better-auth/expo@1.6.2

Current vs. Expected behavior

Current behavior:

  • OAuth sign-in succeeds
  • Better Auth logs signIn was called by user: Sebastian
  • then inside customSession(...), calling auth.api.accountInfo(...) / auth.api.getAccessToken(...) fails with:
Failed to enrich Better Auth custom session [APIError: Account not found] {
  status: 'BAD_REQUEST',
  body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' },
  headers: {},
  statusCode: 400
}
  • authClient.useSession() stays null

Observed logs:

signIn was called by user: Sebastian
LOG  Session data: null
LOG  Session data: null
LOG  Session data: null
LOG  Session data: null
Failed to enrich Better Auth custom session [APIError: Account not found] {
  status: 'BAD_REQUEST',
  body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' },
  headers: {},
  statusCode: 400
}
Failed to enrich Better Auth custom session [APIError: Account not found] {
  status: 'BAD_REQUEST',
  body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' },
  headers: {},
  statusCode: 400
}
LOG  Session data: null
LOG  Session data: null

Expected behavior:

  • after successful Expo OAuth sign-in, customSession(...) should still be able to use:
    • auth.api.accountInfo({ headers: ctx.headers })
    • auth.api.getAccessToken({ body: { providerId: "keycloak" }, headers: ctx.headers })
  • session enrichment should succeed
  • authClient.useSession() should return the enriched session instead of null

What version of Better Auth are you using?

1.6.2

System info

{
  "system": {
    "platform": "linux",
    "arch": "x64",
    "version": "#1 SMP PREEMPT_DYNAMIC Thu Jun  5 18:30:46 UTC 2025",
    "release": "6.6.87.2-microsoft-standard-WSL2",
    "cpuCount": 8,
    "cpuModel": "11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz",
    "totalMemory": "11.68 GB",
    "freeMemory": "5.73 GB"
  },
  "node": {
    "version": "v24.14.1",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.12.1"
  },
  "frameworks": [
    {
      "name": "react",
      "version": "19.2.0"
    }
  ],
  "databases": null,
  "betterAuth": {
    "version": "1.6.2",
    "config": {
      "hooks": {},
      "plugins": [
        {
          "name": "expo",
          "config": {
            "id": "expo",
            "version": "1.6.2",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {}
          }
        },
        {
          "name": "generic-oauth",
          "config": {
            "id": "generic-oauth",
            "version": "1.6.2",
            "endpoints": {},
            "options": {
              "config": [
                {
                  "providerId": "keycloak",
                  "discoveryUrl": "https://keycloak.ilogistics.at/auth/realms/se/.well-known/openid-configuration",
                  "clientId": "[REDACTED]",
                  "clientSecret": "[REDACTED]",
                  "scopes": [
                    "openid",
                    "profile",
                    "email"
                  ],
                  "pkce": true
                }
              ]
            },
            "$ERROR_CODES": {
              "INVALID_OAUTH_CONFIGURATION": {
                "code": "INVALID_OAUTH_CONFIGURATION",
                "message": "Invalid OAuth configuration"
              },
              "TOKEN_URL_NOT_FOUND": {
                "code": "TOKEN_URL_NOT_FOUND",
                "message": "Invalid OAuth configuration. Token URL not found."
              },
              "PROVIDER_CONFIG_NOT_FOUND": {
                "code": "PROVIDER_CONFIG_NOT_FOUND",
                "message": "No config found for provider"
              },
              "PROVIDER_ID_REQUIRED": {
                "code": "PROVIDER_ID_REQUIRED",
                "message": "Provider ID is required"
              },
              "INVALID_OAUTH_CONFIG": {
                "code": "INVALID_OAUTH_CONFIG",
                "message": "Invalid OAuth configuration."
              },
              "SESSION_REQUIRED": {
                "code": "SESSION_REQUIRED",
                "message": "Session is required"
              },
              "ISSUER_MISMATCH": {
                "code": "ISSUER_MISMATCH",
                "message": "OAuth issuer mismatch. The authorization server issuer does not match the expected value (RFC 9207)."
              },
              "ISSUER_MISSING": {
                "code": "ISSUER_MISSING",
                "message": "OAuth issuer parameter missing. The authorization server did not include the required iss parameter (RFC 9207)."
              }
            }
          }
        },
        {
          "name": "custom-session",
          "config": {
            "id": "custom-session",
            "version": "1.6.2",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {},
            "$Infer": {
              "Session": {}
            }
          }
        }
      ],
      "trustedOrigins": [
        "at.co.ill.mobile.ilogistics://"
      ],
      "session": {
        "cookieCache": {
          "enabled": true,
          "maxAge": 7200,
          "strategy": "jwe",
          "refreshCache": true
        }
      },
      "account": {
        "storeStateStrategy": "cookie",
        "storeAccountCookie": true
      }
    }
  }
}

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

Documentation, Backend, Client

Auth config (if applicable)

import 'server-only'
import { type BetterAuthOptions, betterAuth } from 'better-auth'
import { accountInfo, createAuthMiddleware, getAccessToken } from 'better-auth/api'
import { customSession, genericOAuth, keycloak } from 'better-auth/plugins'
import { expo } from '@better-auth/expo'
import { z } from 'zod/v3'
import { authProviderId } from '@/config/constants'
import contextPath from '@/config/contextPath'

const authOptions = {
  hooks: {
    after: createAuthMiddleware(async (ctx) => {
      if (ctx.context.newSession) {
        console.log('signIn was called by user:', ctx.context.newSession.user.name)
      }

      if (ctx.path === '/sign-out') {
        console.log(
          `signOut was called ${ctx.context.session?.user.name ? `by user: ${ctx.context.session.user.name}` : ''}`,
        )
      }
    }),
  },
  plugins: [
    expo(),
    genericOAuth({
      config: [
        {
          ...keycloak({
            clientId: process.env.KEYCLOAK_CLIENT_ID!,
            clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
            issuer: process.env.KEYCLOAK_ISSUER!,
            scopes: ['openid', 'profile', 'email'],
            pkce: true,
          }),
          authorizationUrlParams: (ctx) => {
            const params: Record<string, string> = {}
            const theme = z.enum(['light', 'dark']).safeParse(ctx.body?.additionalData?.theme)
            const loginHint = z.string().safeParse(ctx.body?.additionalData?.loginHint)

            if (theme.success) {
              params.theme = theme.data
            }
            if (loginHint.success) {
              params.login_hint = loginHint.data
            }

            return params
          },
        },
      ],
    }),
  ],
  trustedOrigins: [
    'at.co.ill.mobile.ilogistics://',
    // Development mode - Expo's exp:// scheme with local IP ranges
    ...(process.env.NODE_ENV === 'development'
      ? [
          'exp://*/*', // Trust all Expo development URLs
          'exp://10.0.0.*:*/*', // Trust 10.0.0.x IP range
          'exp://192.168.*.*:*/*', // Trust 192.168.x.x IP range
          'exp://172.*.*.*:*/*', // Trust 172.x.x.x IP range
          'exp://localhost:*/*', // Trust localhost
        ]
      : []),
  ],
  session: {
    cookieCache: {
      enabled: true,
      maxAge: 60 * 60 * 2, // 2 hours
      strategy: process.env.NODE_ENV === 'development' ? 'compact' : 'jwe',
      refreshCache: true,
    },
  },
  account: {
    storeStateStrategy: 'cookie',
    storeAccountCookie: true,
  },
} satisfies BetterAuthOptions

export const auth = betterAuth({
  ...authOptions,
  plugins: [
    ...(authOptions.plugins ?? []),
    customSession(async ({ user, session }, ctx) => {
      try {
        const info = await accountInfo({
          ...ctx,
          returnHeaders: false,
          returnStatus: false,
        })

        if (!info) {
          throw new Error('No Keycloak account info available')
        }

        const tokenResult = await getAccessToken({
          ...ctx,
          method: 'POST',
          body: {
            providerId: authProviderId,
          },
          returnHeaders: false,
          returnStatus: false,
        })

        if (!tokenResult.accessToken) {
          throw new Error('No Keycloak access token available')
        }

        const response = await fetch(`${process.env.CONTEXT_URL_MOBILE}${contextPath.mobile}/user-roles`, {
          headers: {
            Accept: 'application/json',
            Authorization: `Bearer ${tokenResult.accessToken}`,
          },
        })

        if (!response.ok) {
          throw new Error('Failed to fetch user roles')
        }

        const responseData = await response.json()
        const roles = z.string().array().parse(responseData)

        return {
          user: {
            ...user,
            username: String(info.user.id),
            roles,
          },
          session,
        }
      } catch (error) {
        console.error('Failed to enrich Better Auth custom session', error)
        throw error
      }
    }, authOptions),
  ],
})

// Client config:


export const authClient = createAuthClient({
  baseURL: process.env.EXPO_PUBLIC_BETTER_AUTH_URL!,
  plugins: [
    genericOAuthClient(),
    expoClient({
      scheme: Constants.expoConfig?.scheme as string,
      storagePrefix: 'mobile-ilogistics-auth',
      storage: SecureStore,
    }),
    customSessionClient<typeof auth>(),
  ],
})

Additional context

  • This pattern worked for us on 1.4.17:
  • We are aware of PR #7216 and already adjusted lookups to use provider accountId instead of internal DB id where relevant.
  • The confusion is mainly about whether calling auth.api.accountInfo(...) and auth.api.getAccessToken(...) from inside customSession(...) is still a supported pattern in 1.6.2.
  • We could not find documentation clarifying whether this is expected to work in Expo stateless mode, or what the recommended replacement is if it is no longer supported.
  • So this issue is partly a bug report and partly a request for clarification on the intended migration path.
Originally created by @ruff-exec on GitHub (Apr 13, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/9151 ### Is this suited for github? - [x] Yes, this is suited for github ### Reproduction 1. Start from an Expo app using Better Auth with: - `expo()` - `genericOAuth + keycloak` - `customSession(...)` - stateless cookie setup: - `storeStateStrategy: "cookie"` - `storeAccountCookie: true` 2. Inside `customSession(...)`, call: - `auth.api.accountInfo({ headers: ctx.headers })` - `auth.api.getAccessToken({ body: { providerId: "keycloak" }, headers: ctx.headers })` 3. Sign in through Keycloak in the Expo app. 4. Return to the app after successful OAuth redirect. 5. Observe that sign-in itself succeeds, but session enrichment fails and `useSession()` remains `null`. This worked for us before upgrading from: - `better-auth@1.4.17` - `@better-auth/expo@1.4.17` It now fails on: - `better-auth@1.6.2` - `@better-auth/expo@1.6.2` ### Current vs. Expected behavior Current behavior: - OAuth sign-in succeeds - Better Auth logs `signIn was called by user: Sebastian` - then inside `customSession(...)`, calling `auth.api.accountInfo(...)` / `auth.api.getAccessToken(...)` fails with: ```text Failed to enrich Better Auth custom session [APIError: Account not found] { status: 'BAD_REQUEST', body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' }, headers: {}, statusCode: 400 } ``` - `authClient.useSession()` stays `null` Observed logs: ```text signIn was called by user: Sebastian LOG Session data: null LOG Session data: null LOG Session data: null LOG Session data: null Failed to enrich Better Auth custom session [APIError: Account not found] { status: 'BAD_REQUEST', body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' }, headers: {}, statusCode: 400 } Failed to enrich Better Auth custom session [APIError: Account not found] { status: 'BAD_REQUEST', body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' }, headers: {}, statusCode: 400 } LOG Session data: null LOG Session data: null ``` Expected behavior: - after successful Expo OAuth sign-in, `customSession(...)` should still be able to use: - `auth.api.accountInfo({ headers: ctx.headers })` - `auth.api.getAccessToken({ body: { providerId: "keycloak" }, headers: ctx.headers })` - session enrichment should succeed - `authClient.useSession()` should return the enriched session instead of `null` ### What version of Better Auth are you using? 1.6.2 ### System info ```bash { "system": { "platform": "linux", "arch": "x64", "version": "#1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025", "release": "6.6.87.2-microsoft-standard-WSL2", "cpuCount": 8, "cpuModel": "11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz", "totalMemory": "11.68 GB", "freeMemory": "5.73 GB" }, "node": { "version": "v24.14.1", "env": "development" }, "packageManager": { "name": "npm", "version": "11.12.1" }, "frameworks": [ { "name": "react", "version": "19.2.0" } ], "databases": null, "betterAuth": { "version": "1.6.2", "config": { "hooks": {}, "plugins": [ { "name": "expo", "config": { "id": "expo", "version": "1.6.2", "hooks": { "after": [ {} ] }, "endpoints": {} } }, { "name": "generic-oauth", "config": { "id": "generic-oauth", "version": "1.6.2", "endpoints": {}, "options": { "config": [ { "providerId": "keycloak", "discoveryUrl": "https://keycloak.ilogistics.at/auth/realms/se/.well-known/openid-configuration", "clientId": "[REDACTED]", "clientSecret": "[REDACTED]", "scopes": [ "openid", "profile", "email" ], "pkce": true } ] }, "$ERROR_CODES": { "INVALID_OAUTH_CONFIGURATION": { "code": "INVALID_OAUTH_CONFIGURATION", "message": "Invalid OAuth configuration" }, "TOKEN_URL_NOT_FOUND": { "code": "TOKEN_URL_NOT_FOUND", "message": "Invalid OAuth configuration. Token URL not found." }, "PROVIDER_CONFIG_NOT_FOUND": { "code": "PROVIDER_CONFIG_NOT_FOUND", "message": "No config found for provider" }, "PROVIDER_ID_REQUIRED": { "code": "PROVIDER_ID_REQUIRED", "message": "Provider ID is required" }, "INVALID_OAUTH_CONFIG": { "code": "INVALID_OAUTH_CONFIG", "message": "Invalid OAuth configuration." }, "SESSION_REQUIRED": { "code": "SESSION_REQUIRED", "message": "Session is required" }, "ISSUER_MISMATCH": { "code": "ISSUER_MISMATCH", "message": "OAuth issuer mismatch. The authorization server issuer does not match the expected value (RFC 9207)." }, "ISSUER_MISSING": { "code": "ISSUER_MISSING", "message": "OAuth issuer parameter missing. The authorization server did not include the required iss parameter (RFC 9207)." } } } }, { "name": "custom-session", "config": { "id": "custom-session", "version": "1.6.2", "hooks": { "after": [ {} ] }, "endpoints": {}, "$Infer": { "Session": {} } } } ], "trustedOrigins": [ "at.co.ill.mobile.ilogistics://" ], "session": { "cookieCache": { "enabled": true, "maxAge": 7200, "strategy": "jwe", "refreshCache": true } }, "account": { "storeStateStrategy": "cookie", "storeAccountCookie": true } } } } ``` ### Which area(s) are affected? (Select all that apply) Documentation, Backend, Client ### Auth config (if applicable) ```typescript import 'server-only' import { type BetterAuthOptions, betterAuth } from 'better-auth' import { accountInfo, createAuthMiddleware, getAccessToken } from 'better-auth/api' import { customSession, genericOAuth, keycloak } from 'better-auth/plugins' import { expo } from '@better-auth/expo' import { z } from 'zod/v3' import { authProviderId } from '@/config/constants' import contextPath from '@/config/contextPath' const authOptions = { hooks: { after: createAuthMiddleware(async (ctx) => { if (ctx.context.newSession) { console.log('signIn was called by user:', ctx.context.newSession.user.name) } if (ctx.path === '/sign-out') { console.log( `signOut was called ${ctx.context.session?.user.name ? `by user: ${ctx.context.session.user.name}` : ''}`, ) } }), }, plugins: [ expo(), genericOAuth({ config: [ { ...keycloak({ clientId: process.env.KEYCLOAK_CLIENT_ID!, clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!, issuer: process.env.KEYCLOAK_ISSUER!, scopes: ['openid', 'profile', 'email'], pkce: true, }), authorizationUrlParams: (ctx) => { const params: Record<string, string> = {} const theme = z.enum(['light', 'dark']).safeParse(ctx.body?.additionalData?.theme) const loginHint = z.string().safeParse(ctx.body?.additionalData?.loginHint) if (theme.success) { params.theme = theme.data } if (loginHint.success) { params.login_hint = loginHint.data } return params }, }, ], }), ], trustedOrigins: [ 'at.co.ill.mobile.ilogistics://', // Development mode - Expo's exp:// scheme with local IP ranges ...(process.env.NODE_ENV === 'development' ? [ 'exp://*/*', // Trust all Expo development URLs 'exp://10.0.0.*:*/*', // Trust 10.0.0.x IP range 'exp://192.168.*.*:*/*', // Trust 192.168.x.x IP range 'exp://172.*.*.*:*/*', // Trust 172.x.x.x IP range 'exp://localhost:*/*', // Trust localhost ] : []), ], session: { cookieCache: { enabled: true, maxAge: 60 * 60 * 2, // 2 hours strategy: process.env.NODE_ENV === 'development' ? 'compact' : 'jwe', refreshCache: true, }, }, account: { storeStateStrategy: 'cookie', storeAccountCookie: true, }, } satisfies BetterAuthOptions export const auth = betterAuth({ ...authOptions, plugins: [ ...(authOptions.plugins ?? []), customSession(async ({ user, session }, ctx) => { try { const info = await accountInfo({ ...ctx, returnHeaders: false, returnStatus: false, }) if (!info) { throw new Error('No Keycloak account info available') } const tokenResult = await getAccessToken({ ...ctx, method: 'POST', body: { providerId: authProviderId, }, returnHeaders: false, returnStatus: false, }) if (!tokenResult.accessToken) { throw new Error('No Keycloak access token available') } const response = await fetch(`${process.env.CONTEXT_URL_MOBILE}${contextPath.mobile}/user-roles`, { headers: { Accept: 'application/json', Authorization: `Bearer ${tokenResult.accessToken}`, }, }) if (!response.ok) { throw new Error('Failed to fetch user roles') } const responseData = await response.json() const roles = z.string().array().parse(responseData) return { user: { ...user, username: String(info.user.id), roles, }, session, } } catch (error) { console.error('Failed to enrich Better Auth custom session', error) throw error } }, authOptions), ], }) // Client config: export const authClient = createAuthClient({ baseURL: process.env.EXPO_PUBLIC_BETTER_AUTH_URL!, plugins: [ genericOAuthClient(), expoClient({ scheme: Constants.expoConfig?.scheme as string, storagePrefix: 'mobile-ilogistics-auth', storage: SecureStore, }), customSessionClient<typeof auth>(), ], }) ``` ### Additional context - This pattern worked for us on `1.4.17`: - We are aware of PR `#7216` and already adjusted lookups to use provider `accountId` instead of internal DB `id` where relevant. - The confusion is mainly about whether calling `auth.api.accountInfo(...)` and `auth.api.getAccessToken(...)` from inside `customSession(...)` is still a supported pattern in `1.6.2`. - We could not find documentation clarifying whether this is expected to work in Expo stateless mode, or what the recommended replacement is if it is no longer supported. - So this issue is partly a bug report and partly a request for clarification on the intended migration path.
GiteaMirror added the platformoauth labels 2026-04-17 20:02:57 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Apr 13, 2026):

This is a known architectural limitation that became more visible after the account resolution refactoring in v1.6.x. Calling auth.api.accountInfo() and auth.api.getAccessToken() from inside customSession() is effectively an anti-pattern — these methods read from the incoming request headers, but the account cookie may already be expired (default TTL is 5 minutes) or structurally incompatible after the upgrade [1].

What changed between v1.4.17 and v1.6.2:

Several PRs refactored account resolution to use the provider-issued accountId instead of the internal database idPR #7216 fixed getAccessToken/refreshToken, PR #8346 extended that fix to accountInfo, and PR #8786 changed cookie matching logic to compare by provider accountId as well. Cookies set by v1.4.17 may have a structure that v1.6.2 no longer matches correctly.

Recommended fix — use getAccountCookie directly instead of calling API methods:

import { getAccountCookie } from "better-auth/cookies";

customSession(async ({ user, session }, ctx) => {
  const accountData = await getAccountCookie(ctx);

  if (!accountData) {
    console.warn("Account cookie expired or missing");
    return { user, session };
  }

  // accountData contains: accessToken, refreshToken, idToken,
  // accountId, providerId, userId, accessTokenExpiresAt, etc.
  // No need to call auth.api.accountInfo or auth.api.getAccessToken

  return {
    user: {
      ...user,
      username: String(accountData.accountId),
      roles: [], // decode from accountData.accessToken if needed
    },
    session,
  };
}, authOptions),

getAccountCookie was explicitly exported for this exact use case. A user in #6443 confirmed a similar pattern works with Keycloak in stateless mode — reading tokens directly from the account cookie rather than calling API endpoints.

Additional considerations:

  • After upgrading, have users clear cookies and re-authenticate so cookie structure matches the new v1.6.2 format.
  • The account cookie has a 5-minute default TTL and is only refreshed during sign-in or actual token refresh — not on every getAccessToken() call [2]. This means it can silently expire between requests.
  • For production stability, adding a database adapter provides a reliable fallback when cookies expire. Pure stateless mode with storeAccountCookie is fragile by design [2].
  • Keycloak tokens can be large — if they exceed the 4KB cookie limit, chunking may fail silently [3]. Monitor cookie sizes.

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:4236667050 --> @dosubot[bot] commented on GitHub (Apr 13, 2026): <!-- Answer --> This is a known architectural limitation that became more visible after the account resolution refactoring in v1.6.x. Calling `auth.api.accountInfo()` and `auth.api.getAccessToken()` from inside `customSession()` is effectively an anti-pattern — these methods read from the **incoming request headers**, but the account cookie may already be expired (default TTL is 5 minutes) or structurally incompatible after the upgrade [[1]](https://github.com/better-auth/better-auth/issues/7494). **What changed between v1.4.17 and v1.6.2:** Several PRs refactored account resolution to use the provider-issued `accountId` instead of the internal database `id` — [PR #7216](https://github.com/better-auth/better-auth/pull/7216) fixed `getAccessToken`/`refreshToken`, [PR #8346](https://github.com/better-auth/better-auth/pull/8346) extended that fix to `accountInfo`, and [PR #8786](https://github.com/better-auth/better-auth/pull/8786) changed cookie matching logic to compare by provider `accountId` as well. Cookies set by v1.4.17 may have a structure that v1.6.2 no longer matches correctly. **Recommended fix — use `getAccountCookie` directly instead of calling API methods:** ```typescript import { getAccountCookie } from "better-auth/cookies"; customSession(async ({ user, session }, ctx) => { const accountData = await getAccountCookie(ctx); if (!accountData) { console.warn("Account cookie expired or missing"); return { user, session }; } // accountData contains: accessToken, refreshToken, idToken, // accountId, providerId, userId, accessTokenExpiresAt, etc. // No need to call auth.api.accountInfo or auth.api.getAccessToken return { user: { ...user, username: String(accountData.accountId), roles: [], // decode from accountData.accessToken if needed }, session, }; }, authOptions), ``` `getAccountCookie` was [explicitly exported](https://github.com/better-auth/better-auth/pull/8181) for this exact use case. A user in [#6443](https://github.com/better-auth/better-auth/issues/6443#issuecomment-4175927692) confirmed a similar pattern works with Keycloak in stateless mode — reading tokens directly from the account cookie rather than calling API endpoints. **Additional considerations:** - After upgrading, have users **clear cookies and re-authenticate** so cookie structure matches the new v1.6.2 format. - The account cookie has a **5-minute default TTL** and is only refreshed during sign-in or actual token refresh — not on every `getAccessToken()` call [[2]](https://github.com/better-auth/better-auth/discussions/6715). This means it can silently expire between requests. - For production stability, adding a database adapter provides a reliable fallback when cookies expire. Pure stateless mode with `storeAccountCookie` is fragile by design [[2]](https://github.com/better-auth/better-auth/discussions/6715). - Keycloak tokens can be large — if they exceed the 4KB cookie limit, chunking may fail silently [[3]](https://github.com/better-auth/better-auth/issues/6379). Monitor cookie sizes. <!-- 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?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=7ce9978e-9932-4386-96d4-13230068a9be&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=7ce9978e-9932-4386-96d4-13230068a9be&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=7ce9978e-9932-4386-96d4-13230068a9be&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=7ce9978e-9932-4386-96d4-13230068a9be&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=7ce9978e-9932-4386-96d4-13230068a9be&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=7ce9978e-9932-4386-96d4-13230068a9be&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=7ce9978e-9932-4386-96d4-13230068a9be&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&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%20known%20architectural%20limitation%20that%20became%20more%20visible%20after%20the%20account%20resolution%20refactoring%20in%20v1.6.x.%20Calling%20%60auth.api.accountInfo%28%29%60%20and%20%60auth.api.getAccessToken%28%29%60%20from%20inside%20%60customSession%28%29%60%20is%20effectively%20an%20anti-pattern%20%E2%80%94%20these%20methods%20read%20from%20the%20%2A%2Aincoming%20request%20headers%2A%2A%2C%20but%20the%20account%20cookie%20may%20already%20be%20expired%20%28default%20TTL%20is%205%20minutes%29%20or%20structurally%20incompatible%20after%20the%20upgrade%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/issues/7494%29.%0A%0A%2A%2AWhat%20changed%20between%20v1.4.17%20and%20v1.6.2%3A%2A%2A%0A%0ASeveral%20PRs%20refactored%20account%20resolution%20to%20use%20the%20provider-issued%20%60accountId%60%20instead%20of%20the%20internal%20database%20%60id%60%20%E2%80%94%20%5BPR%20%237216%5D%28https%3A//github.com/better-auth/better-auth/pull/7216%29%20fixed%20%60getAccessToken%60/%60refreshToken%60%2C%20%5BPR%20%238346%5D%28https%3A//github.com/better-auth/better-auth/pull/8346%29%20extended%20that%20fix%20to%20%60accountInfo%60%2C%20and%20%5BPR%20%238786%5D%28https%3A//github.com/better-auth/better-auth/pull/8786%29%20changed%20cookie%20matching%20logic%20to%20compare%20by%20provider%20%60accountId%60%20as%20well.%20Cookies%20set%20by%20v1.4.17%20may%20have%20a%20structure%20that%20v1.6.2%20no%20longer%20matches%20correctly.%0A%0A%2A%2ARecommended%20fix%20%E2%80%94%20use%20%60getAccountCookie%60%20directly%20instead%20of%20calling%20API%20methods%3A%2A%2A%0A%0A%60%60%60typescript%0Aimport%20%7B%20getAccountCookie%20%7D%20from%20%22better-auth/cookies%22%3B%0A%0AcustomSession%28async%20%28%7B%20user%2C%20session%20%7D%2C%20ctx%29%20%3D%3E%20%7B%0A%20%20const%20accountData%20%3D%20await%20getAccountCookie%28ctx%29%3B%0A%0A%20%20if%20%28%21accountData%29%20%7B%0A%20%20%20%20console.warn%28%22Account%20cookie%20expired%20or%20missing%22%29%3B%0A%20%20%20%20return%20%7B%20user%2C%20session%20%7D%3B%0A%20%20%7D%0A%0A%20%20//%20accountData%20contains%3A%20accessToken%2C%20refreshToken%2C%20idToken%2C%0A%20%20//%20accountId%2C%20providerId%2C%20userId%2C%20accessTokenExpiresAt%2C%20etc.%0A%20%20//%20No%20need%20to%20call%20auth.api.accountInfo%20or%20auth.api.getAccessToken%0A%0A%20%20return%20%7B%0A%20%20%20%20user%3A%20%7B%0A%20%20%20%20%20%20...user%2C%0A%20%20%20%20%20%20username%3A%20String%28accountData.accountId%29%2C%0A%20%20%20%20%20%20roles%3A%20%5B%5D%2C%20//%20decode%20from%20accountData.accessToken%20if%20needed%0A%20%20%20%20%7D%2C%0A%20%20%20%20session%2C%0A%20%20%7D%3B%0A%7D%2C%20authOptions%29%2C%0A%60%60%60%0A%0A%60getAccountCookie%60%20was%20%5Bexplicitly%20exported%5D%28https%3A//github.com/better-auth/better-auth/pull/8181%29%20for%20this%20exact%20use%20case.%20A%20user%20in%20%5B%236443%5D%28https%3A//github.com/better-auth/better-auth/issues/6443%23issuecomment-4175927692%29%20confirmed%20a%20similar%20pattern%20works%20with%20Keycloak%20in%20stateless%20mode%20%E2%80%94%20reading%20tokens%20directly%20from%20the%20account%20cookie%20rather%20than%20calling%20API%20endpoints.%0A%0A%2A%2AAdditional%20considerations%3A%2A%2A%0A%0A-%20After%20upgrading%2C%20have%20users%20%2A%2Aclear%20cookies%20and%20re-authenticate%2A%2A%20so%20cookie%20structure%20matches%20the%20new%20v1.6.2%20format.%0A-%20The%20account%20cookie%20has%20a%20%2A%2A5-minute%20default%20TTL%2A%2A%20and%20is%20only%20refreshed%20during%20sign-in%20or%20actual%20token%20refresh%20%E2%80%94%20not%20on%20every%20%60getAccessToken%28%29%60%20call%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/discussions/6715%29.%20This%20means%20it%20can%20silently%20expire%20between%20requests.%0A-%20For%20production%20stability%2C%20adding%20a%20database%20adapter%20provides%20a%20reliable%20fallback%20when%20cookies%20expire.%20Pure%20stateless%20mode%20with%20%60storeAccountCookie%60%20is%20fragile%20by%20design%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/discussions/6715%29.%0A-%20Keycloak%20tokens%20can%20be%20large%20%E2%80%94%20if%20they%20exceed%20the%204KB%20cookie%20limit%2C%20chunking%20may%20fail%20silently%20%5B%5B3%5D%5D%28https%3A//github.com/better-auth/better-auth/issues/6379%29.%20Monitor%20cookie%20sizes.)&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/9151)
Author
Owner

@ruff-exec commented on GitHub (Apr 14, 2026):

@dosu

Where do you take the information from that calling auth.api.getAccessToken is not required? I need the access token to fetch from my backend. Why should there be no api call possible for that even if your account solution works?

<!-- gh-comment-id:4241322442 --> @ruff-exec commented on GitHub (Apr 14, 2026): @dosu Where do you take the information from that calling auth.api.getAccessToken is not required? I need the access token to fetch from my backend. Why should there be no api call possible for that even if your account solution works?
Author
Owner

@dosubot[bot] commented on GitHub (Apr 14, 2026):

Fair point — I should clarify. I'm not saying you don't need the access token or that auth.api.getAccessToken shouldn't be called at all. The issue is specifically about calling it from inside customSession() in stateless mode, where it fails because the account cookie it reads from the incoming request headers may be expired or structurally mismatched after the v1.6.x refactoring.

getAccountCookie() returns the full Account object which includes accessToken directly [1] — so inside customSession(), you can read the token without making an API call:

const accountData = await getAccountCookie(ctx);
const accessToken = accountData?.accessToken; // your Keycloak token is here

The difference is that auth.api.getAccessToken() does more than just read the cookie — it also auto-refreshes expired tokens (within 5 seconds of expiry) and updates cookie/DB storage [2]. So it provides real value, just not from inside customSession().

What you can do:

  • Inside customSession(): Use getAccountCookie(ctx) to read the access token directly. This avoids the nested API call that's failing.
  • Outside customSession() (route handlers, API endpoints): auth.api.getAccessToken() works fine and gives you automatic token refresh. This is its intended usage.
  • If you need the token refresh behavior inside session enrichment, you'd need to handle that manually by checking accountData.accessTokenExpiresAt and refreshing through the provider yourself.

So the recommendation isn't "you don't need the access token" — it's "read it directly from the cookie inside customSession() instead of going through the API endpoint that's failing in that context."

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:4241338142 --> @dosubot[bot] commented on GitHub (Apr 14, 2026): <!-- Answer --> Fair point — I should clarify. I'm **not** saying you don't need the access token or that `auth.api.getAccessToken` shouldn't be called at all. The issue is specifically about calling it **from inside `customSession()`** in stateless mode, where it fails because the account cookie it reads from the incoming request headers may be expired or structurally mismatched after the v1.6.x refactoring. `getAccountCookie()` returns the full `Account` object which **includes `accessToken`** directly [[1]](https://github.com/better-auth/better-auth/blob/8dcb98873e939681de822fee69d6bbc439bf7415/packages/core/src/db/schema/account.ts#L10-L33) — so inside `customSession()`, you can read the token without making an API call: ```typescript const accountData = await getAccountCookie(ctx); const accessToken = accountData?.accessToken; // your Keycloak token is here ``` The difference is that `auth.api.getAccessToken()` does more than just read the cookie — it also **auto-refreshes expired tokens** (within 5 seconds of expiry) and updates cookie/DB storage [[2]](https://github.com/better-auth/better-auth/blob/8dcb98873e939681de822fee69d6bbc439bf7415/packages/better-auth/src/api/routes/account.ts#L547-L584). So it provides real value, just not from inside `customSession()`. **What you can do:** - **Inside `customSession()`**: Use `getAccountCookie(ctx)` to read the access token directly. This avoids the nested API call that's failing. - **Outside `customSession()`** (route handlers, API endpoints): `auth.api.getAccessToken()` works fine and gives you automatic token refresh. This is its intended usage. - If you need the token refresh behavior inside session enrichment, you'd need to handle that manually by checking `accountData.accessTokenExpiresAt` and refreshing through the provider yourself. So the recommendation isn't "you don't need the access token" — it's "read it directly from the cookie inside `customSession()` instead of going through the API endpoint that's failing in that context." <!-- 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?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=49c9751b-3013-4009-83eb-cd413c44bd98&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=49c9751b-3013-4009-83eb-cd413c44bd98&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=49c9751b-3013-4009-83eb-cd413c44bd98&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=49c9751b-3013-4009-83eb-cd413c44bd98&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=49c9751b-3013-4009-83eb-cd413c44bd98&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=49c9751b-3013-4009-83eb-cd413c44bd98&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=49c9751b-3013-4009-83eb-cd413c44bd98&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&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=Fair%20point%20%E2%80%94%20I%20should%20clarify.%20I%27m%20%2A%2Anot%2A%2A%20saying%20you%20don%27t%20need%20the%20access%20token%20or%20that%20%60auth.api.getAccessToken%60%20shouldn%27t%20be%20called%20at%20all.%20The%20issue%20is%20specifically%20about%20calling%20it%20%2A%2Afrom%20inside%20%60customSession%28%29%60%2A%2A%20in%20stateless%20mode%2C%20where%20it%20fails%20because%20the%20account%20cookie%20it%20reads%20from%20the%20incoming%20request%20headers%20may%20be%20expired%20or%20structurally%20mismatched%20after%20the%20v1.6.x%20refactoring.%0A%0A%60getAccountCookie%28%29%60%20returns%20the%20full%20%60Account%60%20object%20which%20%2A%2Aincludes%20%60accessToken%60%2A%2A%20directly%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/8dcb98873e939681de822fee69d6bbc439bf7415/packages/core/src/db/schema/account.ts%23L10-L33%29%20%E2%80%94%20so%20inside%20%60customSession%28%29%60%2C%20you%20can%20read%20the%20token%20without%20making%20an%20API%20call%3A%0A%0A%60%60%60typescript%0Aconst%20accountData%20%3D%20await%20getAccountCookie%28ctx%29%3B%0Aconst%20accessToken%20%3D%20accountData%3F.accessToken%3B%20//%20your%20Keycloak%20token%20is%20here%0A%60%60%60%0A%0AThe%20difference%20is%20that%20%60auth.api.getAccessToken%28%29%60%20does%20more%20than%20just%20read%20the%20cookie%20%E2%80%94%20it%20also%20%2A%2Aauto-refreshes%20expired%20tokens%2A%2A%20%28within%205%20seconds%20of%20expiry%29%20and%20updates%20cookie/DB%20storage%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/8dcb98873e939681de822fee69d6bbc439bf7415/packages/better-auth/src/api/routes/account.ts%23L547-L584%29.%20So%20it%20provides%20real%20value%2C%20just%20not%20from%20inside%20%60customSession%28%29%60.%0A%0A%2A%2AWhat%20you%20can%20do%3A%2A%2A%0A%0A-%20%2A%2AInside%20%60customSession%28%29%60%2A%2A%3A%20Use%20%60getAccountCookie%28ctx%29%60%20to%20read%20the%20access%20token%20directly.%20This%20avoids%20the%20nested%20API%20call%20that%27s%20failing.%0A-%20%2A%2AOutside%20%60customSession%28%29%60%2A%2A%20%28route%20handlers%2C%20API%20endpoints%29%3A%20%60auth.api.getAccessToken%28%29%60%20works%20fine%20and%20gives%20you%20automatic%20token%20refresh.%20This%20is%20its%20intended%20usage.%0A-%20If%20you%20need%20the%20token%20refresh%20behavior%20inside%20session%20enrichment%2C%20you%27d%20need%20to%20handle%20that%20manually%20by%20checking%20%60accountData.accessTokenExpiresAt%60%20and%20refreshing%20through%20the%20provider%20yourself.%0A%0ASo%20the%20recommendation%20isn%27t%20%22you%20don%27t%20need%20the%20access%20token%22%20%E2%80%94%20it%27s%20%22read%20it%20directly%20from%20the%20cookie%20inside%20%60customSession%28%29%60%20instead%20of%20going%20through%20the%20API%20endpoint%20that%27s%20failing%20in%20that%20context.%22)&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/9151)
Author
Owner

@ruff-exec commented on GitHub (Apr 14, 2026):

@dosu

Ok, thank you. That fixed it on my side. The issue I now see is this should be documented, so an issue like this does not come up in the future

<!-- gh-comment-id:4241396996 --> @ruff-exec commented on GitHub (Apr 14, 2026): @dosu Ok, thank you. That fixed it on my side. The issue I now see is this should be documented, so an issue like this does not come up in the future
Author
Owner

@dosubot[bot] commented on GitHub (Apr 14, 2026):

Glad that fixed it! And agreed — the documentation should clarify that inside customSession(), you should use getAccountCookie(ctx) directly rather than calling auth.api.accountInfo() or auth.api.getAccessToken(), especially in stateless/Expo setups. The migration from v1.4.x to v1.6.x account ID changes (PRs #7216, #8346, #8786) also deserve a mention in a migration guide.

This issue serves as a good reference for that documentation gap. Keeping it open for the maintainers to track would make sense.

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:4241398165 --> @dosubot[bot] commented on GitHub (Apr 14, 2026): <!-- Answer --> Glad that fixed it! And agreed — the documentation should clarify that inside `customSession()`, you should use `getAccountCookie(ctx)` directly rather than calling `auth.api.accountInfo()` or `auth.api.getAccessToken()`, especially in stateless/Expo setups. The migration from v1.4.x to v1.6.x account ID changes (PRs #7216, #8346, #8786) also deserve a mention in a migration guide. This issue serves as a good reference for that documentation gap. Keeping it open for the maintainers to track would make sense. <!-- 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?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=df001b30-03a8-42a3-9090-2c7d0857345f&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=df001b30-03a8-42a3-9090-2c7d0857345f&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=df001b30-03a8-42a3-9090-2c7d0857345f&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=df001b30-03a8-42a3-9090-2c7d0857345f&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=df001b30-03a8-42a3-9090-2c7d0857345f&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=df001b30-03a8-42a3-9090-2c7d0857345f&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=df001b30-03a8-42a3-9090-2c7d0857345f&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&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=Glad%20that%20fixed%20it%21%20And%20agreed%20%E2%80%94%20the%20documentation%20should%20clarify%20that%20inside%20%60customSession%28%29%60%2C%20you%20should%20use%20%60getAccountCookie%28ctx%29%60%20directly%20rather%20than%20calling%20%60auth.api.accountInfo%28%29%60%20or%20%60auth.api.getAccessToken%28%29%60%2C%20especially%20in%20stateless/Expo%20setups.%20The%20migration%20from%20v1.4.x%20to%20v1.6.x%20account%20ID%20changes%20%28PRs%20%237216%2C%20%238346%2C%20%238786%29%20also%20deserve%20a%20mention%20in%20a%20migration%20guide.%0A%0AThis%20issue%20serves%20as%20a%20good%20reference%20for%20that%20documentation%20gap.%20Keeping%20it%20open%20for%20the%20maintainers%20to%20track%20would%20make%20sense.)&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/9151)
Author
Owner

@ruff-exec commented on GitHub (Apr 14, 2026):

@dosu

I think there is still some more underling issue here, as when I do a request to my expo api route like specified in the docs and then do a

try {
    tokens = await auth.api.getAccessToken({
      body: {
        providerId: authProviderId,
      },
      headers: req.headers,
    })
  } catch (error) {
    console.error('Error fetching access token:', error)

    return new Response(null, {
      status: 401,
    })
  }

in the api route I get the same

Error fetching access token: [APIError: Account not found] {
  status: 'BAD_REQUEST',
  body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' },
  headers: {},
  statusCode: 400
}

So even if in customSession the auth call should not be used, it produces the same error in an API Route with an documented pattern where it should work

<!-- gh-comment-id:4241950635 --> @ruff-exec commented on GitHub (Apr 14, 2026): @dosu I think there is still some more underling issue here, as when I do a request to my expo api route like specified in the [docs](https://better-auth.com/docs/integrations/expo#making-authenticated-requests-to-your-server) and then do a ``` ts try { tokens = await auth.api.getAccessToken({ body: { providerId: authProviderId, }, headers: req.headers, }) } catch (error) { console.error('Error fetching access token:', error) return new Response(null, { status: 401, }) } ``` in the api route I get the same ``` Error fetching access token: [APIError: Account not found] { status: 'BAD_REQUEST', body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' }, headers: {}, statusCode: 400 } ``` So even if in customSession the auth call should not be used, it produces the same error in an API Route with an documented pattern where it should work
Author
Owner

@dosubot[bot] commented on GitHub (Apr 14, 2026):

You're right — auth.api.getAccessToken() called from a regular API route with req.headers should work. This points to a deeper issue: the account_data cookie is likely not present in the request at all, even though the session token is.

In stateless mode, getAccessToken resolves accounts by reading the better-auth.account_data cookie from the incoming request [1]. If that cookie is missing, it falls back to a database query — but in pure stateless mode there's no database, so it throws ACCOUNT_NOT_FOUND.

The session token and account data are independent cookies. You can have a valid session without a valid account_data cookie, which is exactly the pattern you're seeing.

A few things that could prevent the account_data cookie from being available:

  1. Cookie size limit — Keycloak tokens can be large, and the encrypted account_data cookie may exceed the 4KB limit. Unlike session cookies, account_data cookies do not support chunking, so they silently fail to be set [2].

  2. Cookie expiration — The account_data cookie has a 5-minute default TTL and is not automatically refreshed on every request [3] [4].

  3. OAuth redirect handling — During the OAuth callback flow in Expo, Set-Cookie headers may not be properly captured and stored in SecureStore [5].

To diagnose, add this before your getAccessToken call:

console.log('Cookies:', req.headers.get('cookie'));

Check whether better-auth.account_data is present. If it's missing, the cookie was either never set (size issue) or has expired.

Most reliable fix for production: add a database adapter. This gives getAccessToken a fallback when the cookie is missing and eliminates the 5-minute expiry and cookie size constraints entirely. Pure stateless mode with storeAccountCookie has multiple documented limitations that make it fragile for OAuth flows [2] [4].

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:4241992448 --> @dosubot[bot] commented on GitHub (Apr 14, 2026): <!-- Answer --> You're right — `auth.api.getAccessToken()` called from a regular API route with `req.headers` should work. This points to a deeper issue: the `account_data` cookie is likely **not present** in the request at all, even though the session token is. In stateless mode, `getAccessToken` resolves accounts by reading the `better-auth.account_data` cookie from the incoming request [[1]](https://github.com/better-auth/better-auth/blob/513dabb132e2c08a5b6d3b7e88dd397fcd66c1af/packages/better-auth/src/api/routes/account.ts#L523). If that cookie is missing, it falls back to a database query — but in pure stateless mode there's no database, so it throws `ACCOUNT_NOT_FOUND`. The session token and account data are **independent cookies**. You can have a valid session without a valid `account_data` cookie, which is exactly the pattern you're seeing. A few things that could prevent the `account_data` cookie from being available: 1. **Cookie size limit** — Keycloak tokens can be large, and the encrypted `account_data` cookie may exceed the 4KB limit. Unlike session cookies, `account_data` cookies do **not** support chunking, so they silently fail to be set [[2]](https://github.com/better-auth/better-auth/issues/6379). 2. **Cookie expiration** — The `account_data` cookie has a 5-minute default TTL and is not automatically refreshed on every request [[3]](https://github.com/better-auth/better-auth/blob/513dabb132e2c08a5b6d3b7e88dd397fcd66c1af/packages/better-auth/src/cookies/session-store.ts#L284) [[4]](https://github.com/better-auth/better-auth/discussions/6715). 3. **OAuth redirect handling** — During the OAuth callback flow in Expo, `Set-Cookie` headers may not be properly captured and stored in SecureStore [[5]](https://github.com/better-auth/better-auth/issues/6810#issuecomment-3663015897). **To diagnose**, add this before your `getAccessToken` call: ```ts console.log('Cookies:', req.headers.get('cookie')); ``` Check whether `better-auth.account_data` is present. If it's missing, the cookie was either never set (size issue) or has expired. **Most reliable fix for production**: add a database adapter. This gives `getAccessToken` a fallback when the cookie is missing and eliminates the 5-minute expiry and cookie size constraints entirely. Pure stateless mode with `storeAccountCookie` has multiple documented limitations that make it fragile for OAuth flows [[2]](https://github.com/better-auth/better-auth/issues/6379) [[4]](https://github.com/better-auth/better-auth/discussions/6715). <!-- 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?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=6c38d109-786c-4fb4-819b-9141cb4f0766&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=6c38d109-786c-4fb4-819b-9141cb4f0766&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=6c38d109-786c-4fb4-819b-9141cb4f0766&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=6c38d109-786c-4fb4-819b-9141cb4f0766&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=6c38d109-786c-4fb4-819b-9141cb4f0766&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=6c38d109-786c-4fb4-819b-9141cb4f0766&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=6c38d109-786c-4fb4-819b-9141cb4f0766&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&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=You%27re%20right%20%E2%80%94%20%60auth.api.getAccessToken%28%29%60%20called%20from%20a%20regular%20API%20route%20with%20%60req.headers%60%20should%20work.%20This%20points%20to%20a%20deeper%20issue%3A%20the%20%60account_data%60%20cookie%20is%20likely%20%2A%2Anot%20present%2A%2A%20in%20the%20request%20at%20all%2C%20even%20though%20the%20session%20token%20is.%0A%0AIn%20stateless%20mode%2C%20%60getAccessToken%60%20resolves%20accounts%20by%20reading%20the%20%60better-auth.account_data%60%20cookie%20from%20the%20incoming%20request%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/513dabb132e2c08a5b6d3b7e88dd397fcd66c1af/packages/better-auth/src/api/routes/account.ts%23L523%29.%20If%20that%20cookie%20is%20missing%2C%20it%20falls%20back%20to%20a%20database%20query%20%E2%80%94%20but%20in%20pure%20stateless%20mode%20there%27s%20no%20database%2C%20so%20it%20throws%20%60ACCOUNT_NOT_FOUND%60.%0A%0AThe%20session%20token%20and%20account%20data%20are%20%2A%2Aindependent%20cookies%2A%2A.%20You%20can%20have%20a%20valid%20session%20without%20a%20valid%20%60account_data%60%20cookie%2C%20which%20is%20exactly%20the%20pattern%20you%27re%20seeing.%0A%0AA%20few%20things%20that%20could%20prevent%20the%20%60account_data%60%20cookie%20from%20being%20available%3A%0A%0A1.%20%2A%2ACookie%20size%20limit%2A%2A%20%E2%80%94%20Keycloak%20tokens%20can%20be%20large%2C%20and%20the%20encrypted%20%60account_data%60%20cookie%20may%20exceed%20the%204KB%20limit.%20Unlike%20session%20cookies%2C%20%60account_data%60%20cookies%20do%20%2A%2Anot%2A%2A%20support%20chunking%2C%20so%20they%20silently%20fail%20to%20be%20set%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/issues/6379%29.%0A%0A2.%20%2A%2ACookie%20expiration%2A%2A%20%E2%80%94%20The%20%60account_data%60%20cookie%20has%20a%205-minute%20default%20TTL%20and%20is%20not%20automatically%20refreshed%20on%20every%20request%20%5B%5B3%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/513dabb132e2c08a5b6d3b7e88dd397fcd66c1af/packages/better-auth/src/cookies/session-store.ts%23L284%29%20%5B%5B4%5D%5D%28https%3A//github.com/better-auth/better-auth/discussions/6715%29.%0A%0A3.%20%2A%2AOAuth%20redirect%20handling%2A%2A%20%E2%80%94%20During%20the%20OAuth%20callback%20flow%20in%20Expo%2C%20%60Set-Cookie%60%20headers%20may%20not%20be%20properly%20captured%20and%20stored%20in%20SecureStore%20%5B%5B5%5D%5D%28https%3A//github.com/better-auth/better-auth/issues/6810%23issuecomment-3663015897%29.%0A%0A%2A%2ATo%20diagnose%2A%2A%2C%20add%20this%20before%20your%20%60getAccessToken%60%20call%3A%0A%0A%60%60%60ts%0Aconsole.log%28%27Cookies%3A%27%2C%20req.headers.get%28%27cookie%27%29%29%3B%0A%60%60%60%0A%0ACheck%20whether%20%60better-auth.account_data%60%20is%20present.%20If%20it%27s%20missing%2C%20the%20cookie%20was%20either%20never%20set%20%28size%20issue%29%20or%20has%20expired.%0A%0A%2A%2AMost%20reliable%20fix%20for%20production%2A%2A%3A%20add%20a%20database%20adapter.%20This%20gives%20%60getAccessToken%60%20a%20fallback%20when%20the%20cookie%20is%20missing%20and%20eliminates%20the%205-minute%20expiry%20and%20cookie%20size%20constraints%20entirely.%20Pure%20stateless%20mode%20with%20%60storeAccountCookie%60%20has%20multiple%20documented%20limitations%20that%20make%20it%20fragile%20for%20OAuth%20flows%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/issues/6379%29%20%5B%5B4%5D%5D%28https%3A//github.com/better-auth/better-auth/discussions/6715%29.)&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/9151)
Author
Owner

@ruff-exec commented on GitHub (Apr 14, 2026):

@dosu

No, this is the full log with the headers:

Android Bundled 342ms node_modules/expo-router/entry.js (1 module)
Android Bundled 98ms node_modules/expo-network/build/Network.js (1 module)
Android Bundled 64ms node_modules/expo-web-browser/build/WebBrowser.js (1 module)
signIn was called by user: Sebastian
Received GET request for http://localhost:8081/api/proxy/mobile-ilogistics/menu-items
Request headers: {
  accept: 'application/json',
  'accept-encoding': 'gzip',
  connection: 'Keep-Alive',
  cookie: 'better-auth.account_data.0=eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwia2lkIjoiNllNTks1QUJxTm94QTNYS1BObVpOTEdDY2JqYWRLUUZSVkZGOWp2Rk9PbyJ9..zhmuUlNB2tKihKpJTGl-LA.5xGorQqFQo5cYuHxb7nXcQJYKjpiR0NoEL00CPnwm1Nf02t-3zl0QDJGnux9dJm22Rqca2t0aWX9eQaGE8cRns29oHNO1NjnHISRV5X5g6T6gX3a8xb7p9l-1oO0oBS5Gib4VhtrupWf_u_Xl8QR2R3idj8P59phiEnBi0ao5d_mc9wbNDpxEn3Rnqaf9q288J26DoCuYkSZyRY09u7Gt26Yc6KA5CWLZbRI3Tdf_5xU6KHplV0RfJS6aMnOYeyd5zdrXejFrOWM7d7ixyd_4-c95Hj--2nApnfK2Om8Vf_VxRns8kB46KB2GXtPp3OkoyTvYbQKmZmZPjvc-dM0XQ0bjH67wXMkWQzXgOVfbI9pVeYo_Hja1Rzz8S8wugbXkOdqETEL33v9matz57MMRdqEsKgOd9vdHBphXB3xisUZWes-9NT42MZ2UgRVdRJF3toXpTJJuR59q7iCYChUmd1VHR_5UlwPnswhETMkxCa5QTk6xhifR_t1ZN139c3dzPwrGrfezNsWTCwdckF0xzZ3rMXUzoUY2FHI3Tu99QbnOfOC2KKohYiMUIzdkzQkGRBEpO07Appx9Cw1z_YcozWAsUIMTcHCPwAOWcziKGQt38NyCAQUf93yMj9_AL9blSO-1dAaIt8kwxhDPBmzcQUB9awxba1X6UK_ZsduMUlS2Nvmc31qYbNkDZ06atl6wbSkMtqog6pkJpdEh9NeEp8cGBCdkgpNc6W6QyuSgUzgJCl4DdIjgBV3cPt2O3ncZ-QhC1yeMx9PFVn40CuIxNutFB9ZkcUhJEGaxajnezqFkuY24LOCws7dNjJKBgIPXx3FY55xlNeRkT6GaQ_bKXSd1-9h_xYB1BxRqwJPTacQ2TAUuRRDe0DEfFKtXNIx_ipOoUYgLZ5_ISgcelX2QJo3HXw6af3mix17DS04CELsiJD0k4R8UWv0mAG9KAU-txJ1AZVdoiMS5jsK-8FnCevSzO6eH69O9TsY65eRFAJ-3uS5V-q8NULtz5m9pU3EvyBDYHXEuC-WBPLPSzvIoMXmVYOKUYXYRJrheYvwlN0jJ4lIPdXU3c_Qv__8ynvJEibZBs_fMrxSjxeiZkKDQJULPW-0BTxkJ45cNTLlJB8jZfmTXU1gnp4PzYKs7LGcg6ECh1vBJb3lOBXlWC_nnu_hdGLT7H5Ki76VHsxx8TXK_LVng7kXRRqznGHvdoZRJJNu7vdiBO7vBtrv_A28PdFVFjIi-N84HirU_iM4N-O3TZOEs0MXxwKSa7haFXegqMWhRu84qsu1Xq7bYwo4Bzvk3PcOxBOCAX5doFu0xMV5MEWpqvDrxLT0tY1EouIW-yH3zI5nDrKBMICG00kuwtEzt_3Scjs_u91g7BfwuaFNcKYL1p0WpdwnXccH3QjEL-96CF_3H8xsiEOewDqVbLB6v-2PDXN9nzRoSqXkLxzRrDfNd63JtSHuZI6MlAEKGZLodsRi01JSMb9PysW4v7BzmYxKRqOiwpXJQZQWgCINT370mosnCjxDEJa1LvfYRaKpyAgoOerG_-hq9U7d4yTEQfcPQfmUO9OzkzG6f-xUeHzmoaWBtMMYH975ix3Gc1JnetH--DJSHFPJCQvj65IcHmwRmqyE6eaYr0Q-YGxrIAcY-VW7w1UdCcQ3pfApzWBrVLDUWNvh41n2ndztswopzh0jKASPIwPu0Tr1PVzteIk4E6N03idABJCxhdxne29jxzahXNErIPgjT323_Snv0MRMgc_hgZNkULa-gGdDVtsQJScr6YyOEeaQHuzGmKUUis5foviv08mE4fsXGf4kZvgk2NT1V4Xh7oWZkDrwGurmys_GW90_jnk03qJQzfSNLJXNq82QmfbsD3T-qg2n6V5LcPvlNYAS_M3QfPKXM0sD2cfKcVJrZ1RaYPmI2AOy7JicwSoC1iTz9Rzn6gkJEJTQ3nUznbRBzMWo4XDtLD-tkccaNUTUzOKhGn4NEB661O5zAQHvkj4q5blkv2x2WCdMIgD2RxL-97ryYVmHfLoeZUQYhj3xSdGU2r7ZnqZ2uElL9Iyon6uQY80zZYbHJBx2WwYznprTW1o3sO9Qd0I2HgkBa8s_1Ac8MJK8xd8-xY7wUNmMwA8iIR7e_EOi86hfPEIEGZX6NobSmi-pNqDXpasB-tE6ioDacfBArAYje2Vbe2g04Sstwn5r4qV7EovSQQ3rwpg71wYswTz8PnRhHckjLHKOFAiAXjSZ45SkJJbcQk-R9T8SQtb9ympOLhRwhEngxlk62RRLfYmaFdT9LeQlgUbMfvqtdiZuhJ43tok46wua8y62X-Vw3vhqE8okBPxMTSZlkqp5lMfFL9H0Gp2aKfeTLgXxwqWgUObWOZANcbSSl8TmSXjL6FCZPMkbUvvveFiPfPLc-D3fVVjSRY3832w43bMsB6IJs7HrGvEcmp_gCRZKuhxNX3NHbZVEJw6owLmLhgk7WArXAE5umSkbEGrE9Kgjb_aOCh92AVZl41vTISAM6lw06EJRKCIKzfGUxikBLVVoJrvlk8zgr4Hy0n4BddD12k23tXF1ZXngggRMybaol09De6SHdX-XrwUCj-11iYtsyv4SSS3M7jF254RmU_rq6ZBUg-4Beojj_JnRsZDF1cH0THFm8IKd2WaCwJloq3aImKnn6VuXreF60OV2WAb68nSzCK_G58jZobBkMn0878uSXEw1-2vt2dyjXQ2miot2RHfK_JHMOFabNfXBTDhcr5MEDD9Q9X4YhSdcqj1VK36G0nVtudrnh09-A2HzwA5IipLlIivZpqfFgExBySmR5vtLaudTYVHQN55KaVcPFBS2G403JhWGski2jykexyHqOCkb2Okz3E0rE9w7RNfKG-ApEl9nYK26DuGhue9I6sJOWtoXlbz7rL9Yv-6Sosf6ed6WQwJvNGNegvHa3HkQhUDM3M7yD1GVt8IzUoqLrta_eBNoTgRdDoLn57Oy63j1eOcQCl56EUlgo-CpyUHfVO9szhm1Rg_VGh4PKALHrm7_IC7j6oRxmS2kzVGkOBsjDhji39HrkiKiVttQXgHWUwSRQo4Q4CoWTRR_uVgVuKolRaf5QaBCBmRc501Uexy0Pqd-15fRFTZ5KmCg08YHilmW44tSI9sAD6PnVtJxbCIN8pxNfJs8s48-wS_yvlAqr3NRxS9XhDlin892MDxz70Vi94PY5_waCRocRPTEFbAU2Lqj2xUk19RsUxtQ3VdHRn9UpwH_1xxzfoP-2eaGtSAlFLinpua_lzkqtt1ockwXAlvVAQUFevV8-eEcfBAHenTWRDA9l7J48Hp3mAaVNrR67oNk7YOeQSNHzpa6U8fwf0QKjZDW3553W79VP-Kz7GF-ZUzTZDDs_FOxYjn2xQB6Ar7j1R9FYF_jTzEsCp9xLhRG3uqp9eWbMdJNLgSZQmeSfMyfCVKmqunSc1fdUhYcBd6qMwbXznCYBpkW8dUw8y43SFo9ZRMdEun8_sfSluSMNei-aRJB9She_EsKvb4zUIpBdvsTTjyRjNc4rLJ1NNMvMBNWXF5riT5foEnZDNoZ6Gie0rlC3mcf-eW3IxYVp6l5_yGMyDQUlGOoHWd3ael-Fip5oxm9NEi2C0WcVNBUcFJG895HVT2dLVfP8ElauxCUWECYz7zA8FLO9CZ284FX_aMCwVWBQG_BOzsQI8ABz5AqJaQH3Bp7bu6mzpfUC3QDbPl16nE60L6cOShuxosn-AdOXxiqoFMj13MIPfC; better-auth.account_data.1=ezV4roPD_SaU-nf2mrkTnhjXJaxtzA4cYsle76Qinc5Zzh5ZTbgZTzHfaY3I6X3Bkl8We5GqWq3U3QWPKsXUd0ERiwDP0K8YmqB38tf603evfo5ogY_FhxB0tVd93D-J0WqFasO2_-Ts9hQldoq221dmpJfrgG25lB0PBkjp4trfkleauGnds2MbDx9gEhvx8eL5_Q7nPjBTxUsAdhBFfBvlJvqda__vNRMwlUxXynHJ-PhMMXhC57vyTgVw25Uly2ODLbvZD2st6pMyxoXqmqh4-wbfC8bVH1iibHFFuhRtnqzkeCFQaRaz5sTnQHb2wfD3y-n1YoUn_x9gl0YfAPE6JMgz5afMP_ZxX1ofdw2uK6Qm6AHXzVf4TOsYVVIy-6sRXECQC7WkPC8Nvdr-vVIiCjrdwdjCr9IqVEv1DT4rJtQsz-aY-3sR1nRxMWHPDcNdoV2VOXBkYu3JAg-qruyWsiy45WCakCwBdIrZRA18tRm-T8YlmLjaw8ZVKr_ZTv9kteeI94M23awWIlgKlnm7L7ezmIQYw6HhKYxBB7EK-Nqa6lwDdUDgS6G09zsTLjxE7LWl52p9qvVOrx2qnfuCYM2eRKsCZmHyTK9ls6UiFjYwPWsKTN6cdeoGLCTlmKNjRGFZkdRZX0MuRmgQkmUZxWqlENdpj40qSbI7JTpyTdAwP33MX82CCLjifynZgWfME_hLoE9vyozzgLbuyfPIkFhOHSsZBc4DILkpBcOgiT37KlWXjvEVHm21Xel1bNR7tTTJuITEO8hI608BrmcYPTZsAFlGNnSw6KCvD4sbi49P2z0akWqMPiz_wtU2J45j2w-C8lWRRNfRn46cBJl4SUBIWQfTsi0Zv9AcJ1OZNtNHSAqIfNLwumfallcoImW1tqvj5Nn6N2VrUKBsCcttU1Kzbtf9sGfL49yEphbVvE_OgDsunGo_SXXGXJbna0kdY_vEvdUpfk5d5x9I4wET6XoRiZ4AE_DXaGokPLU8ldPuRYSVkAXg2edy4dB9C6QUVSV_mjXx4ZTuDhirnm8eEBrJjuKNuANWy6PAj1uKKMce5w_eDJzzlnFZwU3LNRpE9Jgip2XVipO6kycTV76jfj42hrVq9KTcILu0RamyiqEOloSgF1I0mm9JBRUeWkChSy0YJ49Hyge252ez2RW1u-G8zjPmOqf-RSP7UKvbAyw9q7X0Doh87sIn99Aiv5bnh5cTNl2d1pZXs1VotvdCVkZmKeTAieUOtM94pasKCk8ca0oo1-FMOri93R1U4cCxVWeXwHJx2DvqyIYKo2zE6KN0BKt0a7ZJVUO15RX-nec84236ZyuZDC_o1yDZMjF8tA-WLvOhrdYaEx4qcFKZwjvnQkD08HWskMzzpgrYe024C3G_PF2_iMAPllZ7oG5OfCzJKnze_PdGeGpK6KCBi2JWXGvE7hP8H3au_jrEdzjzTpi143tAhJ7JmusLHP9gYW_6hrzwY98sUeosg6T5iCFecUronNHVb05uGPh-BVsMjp7zCuZ44TmrMlIa4rd3kEdD4lqNeT5AdO8eE8JTbKE1WHed5hiJga3tgJrdnga-H7HFXt8Hmu0Za9qGrX21FL3IHnXWpHfu9VJz14q3sqFNwFIQRsDMzAvgmxgB66FPYzB7NWin80P1VZo-QZvD4Q-mqYWUPsCod7NBNqEruK5BZi5iBGjeTx3btmCe6V5FbTTnlhQzfLh4Rr4mxaFPRnLh_S_soE4IFGkDDuy5V-8FLarWnZTxl-O1mFmd0nHAI9ak8RlTR39ooDLVxAWvUmqhZ-G_7lSUm0dWgkOEqHfBU_n4uJxDaFeEsboiU79kOxOoWRQo0GwEifaVLb-4jB0pUU-OsD3DUMrfHZqvAVv_xdn9VGBunTcl0Zb6fl0HdxXRuXlBdLXIkwj17J-9JM-5CpaMz-0_Z8nQZ3z3GYlOKszl5LpVH5c4sf1fI9wMBEIhtOaX6LkFJNlvCN-imemZHR4nYGLETvoh28J7sdddBd62aZounMjgNRxduCJfCNHbv9BRviWIKeKeTb0be8QLU-5q9UjBDKfe3o8Gn_eiF3pT7XmDmGQ1Yuip4M8-nDWsgHoc7lo3AivyLfcImUOv01recKNYx_qETKAZuVIvRKRoXtY7S3VAQZUHPVGsFOAH5uvb6h6S2xIuONiK3733F4naiaj5dTv8Z5SJ9atv5fpx8f6ycgAlvsA.fZ93Zkyy6orWvwEnJGDD4yZd6OCkuQD7rxTNcT8C48s; better-auth.session_token=MkI9dsc3pxdQSZBX0svK0R8fXaRlfvLR.vyrnwrTogBxTaIUO49M3A1fjkAV7FA5Vhh1mSH1SIV4=; better-auth.session_data=eyJzZXNzaW9uIjp7InNlc3Npb24iOnsiZXhwaXJlc0F0IjoiMjAyNi0wNC0yMVQwNjo1NTowNy44NDNaIiwidG9rZW4iOiJNa0k5ZHNjM3B4ZFFTWkJYMHN2SzBSOGZYYVJsZnZMUiIsImNyZWF0ZWRBdCI6IjIwMjYtMDQtMTRUMDY6NTU6MDcuODQ0WiIsInVwZGF0ZWRBdCI6IjIwMjYtMDQtMTRUMDY6NTU6MDcuODQ0WiIsImlwQWRkcmVzcyI6IjEyNy4wLjAuMSIsInVzZXJBZ2VudCI6Ik1vemlsbGEvNS4wIChMaW51eDsgQW5kcm9pZCAxMDsgSykgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzE0Ni4wLjAuMCBNb2JpbGUgU2FmYXJpLzUzNy4zNiIsInVzZXJJZCI6InpvakdwZ2g5bkJKbHZkbHZZQ0dnd3RYZkhiek9NQ1UzIiwiaWQiOiJsSWhlM3ZYZjJhUXlmbGQ5SmIxMlRsUzIzeFhqQWNJeSJ9LCJ1c2VyIjp7Im5hbWUiOiJTZWJhc3RpYW4iLCJlbWFpbCI6InJ1ZmZAaWxsLmNvLmF0IiwiZW1haWxWZXJpZmllZCI6dHJ1ZSwiY3JlYXRlZEF0IjoiMjAyNi0wNC0xNFQwNjo1NTowNy44NDFaIiwidXBkYXRlZEF0IjoiMjAyNi0wNC0xNFQwNjo1NTowNy44NDFaIiwiaWQiOiJ6b2pHcGdoOW5CSmx2ZGx2WUNHZ3d0WGZIYnpPTUNVMyJ9LCJ1cGRhdGVkQXQiOjE3NzYxNDk3MDc4NDQsInZlcnNpb24iOiIxIn0sImV4cGlyZXNBdCI6MTc3NjE1NjkwNzg0NCwic2lnbmF0dXJlIjoiSUNlQ1ZEQ18xNTVqbzNqSnpBNTNGNzRpMUhkZTZHVkhYUzBHTkRvMnI5dyJ9',
  host: 'localhost:8081',
  'user-agent': 'okhttp/4.12.0'
}
Error fetching access token: [APIError: Account not found] {
  status: 'BAD_REQUEST',
  body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' },
  headers: {},
  statusCode: 400
}
signOut was called
<!-- gh-comment-id:4242020917 --> @ruff-exec commented on GitHub (Apr 14, 2026): @dosu No, this is the full log with the headers: ``` Android Bundled 342ms node_modules/expo-router/entry.js (1 module) Android Bundled 98ms node_modules/expo-network/build/Network.js (1 module) Android Bundled 64ms node_modules/expo-web-browser/build/WebBrowser.js (1 module) signIn was called by user: Sebastian Received GET request for http://localhost:8081/api/proxy/mobile-ilogistics/menu-items Request headers: { accept: 'application/json', 'accept-encoding': 'gzip', connection: 'Keep-Alive', cookie: 'better-auth.account_data.0=eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwia2lkIjoiNllNTks1QUJxTm94QTNYS1BObVpOTEdDY2JqYWRLUUZSVkZGOWp2Rk9PbyJ9..zhmuUlNB2tKihKpJTGl-LA.5xGorQqFQo5cYuHxb7nXcQJYKjpiR0NoEL00CPnwm1Nf02t-3zl0QDJGnux9dJm22Rqca2t0aWX9eQaGE8cRns29oHNO1NjnHISRV5X5g6T6gX3a8xb7p9l-1oO0oBS5Gib4VhtrupWf_u_Xl8QR2R3idj8P59phiEnBi0ao5d_mc9wbNDpxEn3Rnqaf9q288J26DoCuYkSZyRY09u7Gt26Yc6KA5CWLZbRI3Tdf_5xU6KHplV0RfJS6aMnOYeyd5zdrXejFrOWM7d7ixyd_4-c95Hj--2nApnfK2Om8Vf_VxRns8kB46KB2GXtPp3OkoyTvYbQKmZmZPjvc-dM0XQ0bjH67wXMkWQzXgOVfbI9pVeYo_Hja1Rzz8S8wugbXkOdqETEL33v9matz57MMRdqEsKgOd9vdHBphXB3xisUZWes-9NT42MZ2UgRVdRJF3toXpTJJuR59q7iCYChUmd1VHR_5UlwPnswhETMkxCa5QTk6xhifR_t1ZN139c3dzPwrGrfezNsWTCwdckF0xzZ3rMXUzoUY2FHI3Tu99QbnOfOC2KKohYiMUIzdkzQkGRBEpO07Appx9Cw1z_YcozWAsUIMTcHCPwAOWcziKGQt38NyCAQUf93yMj9_AL9blSO-1dAaIt8kwxhDPBmzcQUB9awxba1X6UK_ZsduMUlS2Nvmc31qYbNkDZ06atl6wbSkMtqog6pkJpdEh9NeEp8cGBCdkgpNc6W6QyuSgUzgJCl4DdIjgBV3cPt2O3ncZ-QhC1yeMx9PFVn40CuIxNutFB9ZkcUhJEGaxajnezqFkuY24LOCws7dNjJKBgIPXx3FY55xlNeRkT6GaQ_bKXSd1-9h_xYB1BxRqwJPTacQ2TAUuRRDe0DEfFKtXNIx_ipOoUYgLZ5_ISgcelX2QJo3HXw6af3mix17DS04CELsiJD0k4R8UWv0mAG9KAU-txJ1AZVdoiMS5jsK-8FnCevSzO6eH69O9TsY65eRFAJ-3uS5V-q8NULtz5m9pU3EvyBDYHXEuC-WBPLPSzvIoMXmVYOKUYXYRJrheYvwlN0jJ4lIPdXU3c_Qv__8ynvJEibZBs_fMrxSjxeiZkKDQJULPW-0BTxkJ45cNTLlJB8jZfmTXU1gnp4PzYKs7LGcg6ECh1vBJb3lOBXlWC_nnu_hdGLT7H5Ki76VHsxx8TXK_LVng7kXRRqznGHvdoZRJJNu7vdiBO7vBtrv_A28PdFVFjIi-N84HirU_iM4N-O3TZOEs0MXxwKSa7haFXegqMWhRu84qsu1Xq7bYwo4Bzvk3PcOxBOCAX5doFu0xMV5MEWpqvDrxLT0tY1EouIW-yH3zI5nDrKBMICG00kuwtEzt_3Scjs_u91g7BfwuaFNcKYL1p0WpdwnXccH3QjEL-96CF_3H8xsiEOewDqVbLB6v-2PDXN9nzRoSqXkLxzRrDfNd63JtSHuZI6MlAEKGZLodsRi01JSMb9PysW4v7BzmYxKRqOiwpXJQZQWgCINT370mosnCjxDEJa1LvfYRaKpyAgoOerG_-hq9U7d4yTEQfcPQfmUO9OzkzG6f-xUeHzmoaWBtMMYH975ix3Gc1JnetH--DJSHFPJCQvj65IcHmwRmqyE6eaYr0Q-YGxrIAcY-VW7w1UdCcQ3pfApzWBrVLDUWNvh41n2ndztswopzh0jKASPIwPu0Tr1PVzteIk4E6N03idABJCxhdxne29jxzahXNErIPgjT323_Snv0MRMgc_hgZNkULa-gGdDVtsQJScr6YyOEeaQHuzGmKUUis5foviv08mE4fsXGf4kZvgk2NT1V4Xh7oWZkDrwGurmys_GW90_jnk03qJQzfSNLJXNq82QmfbsD3T-qg2n6V5LcPvlNYAS_M3QfPKXM0sD2cfKcVJrZ1RaYPmI2AOy7JicwSoC1iTz9Rzn6gkJEJTQ3nUznbRBzMWo4XDtLD-tkccaNUTUzOKhGn4NEB661O5zAQHvkj4q5blkv2x2WCdMIgD2RxL-97ryYVmHfLoeZUQYhj3xSdGU2r7ZnqZ2uElL9Iyon6uQY80zZYbHJBx2WwYznprTW1o3sO9Qd0I2HgkBa8s_1Ac8MJK8xd8-xY7wUNmMwA8iIR7e_EOi86hfPEIEGZX6NobSmi-pNqDXpasB-tE6ioDacfBArAYje2Vbe2g04Sstwn5r4qV7EovSQQ3rwpg71wYswTz8PnRhHckjLHKOFAiAXjSZ45SkJJbcQk-R9T8SQtb9ympOLhRwhEngxlk62RRLfYmaFdT9LeQlgUbMfvqtdiZuhJ43tok46wua8y62X-Vw3vhqE8okBPxMTSZlkqp5lMfFL9H0Gp2aKfeTLgXxwqWgUObWOZANcbSSl8TmSXjL6FCZPMkbUvvveFiPfPLc-D3fVVjSRY3832w43bMsB6IJs7HrGvEcmp_gCRZKuhxNX3NHbZVEJw6owLmLhgk7WArXAE5umSkbEGrE9Kgjb_aOCh92AVZl41vTISAM6lw06EJRKCIKzfGUxikBLVVoJrvlk8zgr4Hy0n4BddD12k23tXF1ZXngggRMybaol09De6SHdX-XrwUCj-11iYtsyv4SSS3M7jF254RmU_rq6ZBUg-4Beojj_JnRsZDF1cH0THFm8IKd2WaCwJloq3aImKnn6VuXreF60OV2WAb68nSzCK_G58jZobBkMn0878uSXEw1-2vt2dyjXQ2miot2RHfK_JHMOFabNfXBTDhcr5MEDD9Q9X4YhSdcqj1VK36G0nVtudrnh09-A2HzwA5IipLlIivZpqfFgExBySmR5vtLaudTYVHQN55KaVcPFBS2G403JhWGski2jykexyHqOCkb2Okz3E0rE9w7RNfKG-ApEl9nYK26DuGhue9I6sJOWtoXlbz7rL9Yv-6Sosf6ed6WQwJvNGNegvHa3HkQhUDM3M7yD1GVt8IzUoqLrta_eBNoTgRdDoLn57Oy63j1eOcQCl56EUlgo-CpyUHfVO9szhm1Rg_VGh4PKALHrm7_IC7j6oRxmS2kzVGkOBsjDhji39HrkiKiVttQXgHWUwSRQo4Q4CoWTRR_uVgVuKolRaf5QaBCBmRc501Uexy0Pqd-15fRFTZ5KmCg08YHilmW44tSI9sAD6PnVtJxbCIN8pxNfJs8s48-wS_yvlAqr3NRxS9XhDlin892MDxz70Vi94PY5_waCRocRPTEFbAU2Lqj2xUk19RsUxtQ3VdHRn9UpwH_1xxzfoP-2eaGtSAlFLinpua_lzkqtt1ockwXAlvVAQUFevV8-eEcfBAHenTWRDA9l7J48Hp3mAaVNrR67oNk7YOeQSNHzpa6U8fwf0QKjZDW3553W79VP-Kz7GF-ZUzTZDDs_FOxYjn2xQB6Ar7j1R9FYF_jTzEsCp9xLhRG3uqp9eWbMdJNLgSZQmeSfMyfCVKmqunSc1fdUhYcBd6qMwbXznCYBpkW8dUw8y43SFo9ZRMdEun8_sfSluSMNei-aRJB9She_EsKvb4zUIpBdvsTTjyRjNc4rLJ1NNMvMBNWXF5riT5foEnZDNoZ6Gie0rlC3mcf-eW3IxYVp6l5_yGMyDQUlGOoHWd3ael-Fip5oxm9NEi2C0WcVNBUcFJG895HVT2dLVfP8ElauxCUWECYz7zA8FLO9CZ284FX_aMCwVWBQG_BOzsQI8ABz5AqJaQH3Bp7bu6mzpfUC3QDbPl16nE60L6cOShuxosn-AdOXxiqoFMj13MIPfC; better-auth.account_data.1=ezV4roPD_SaU-nf2mrkTnhjXJaxtzA4cYsle76Qinc5Zzh5ZTbgZTzHfaY3I6X3Bkl8We5GqWq3U3QWPKsXUd0ERiwDP0K8YmqB38tf603evfo5ogY_FhxB0tVd93D-J0WqFasO2_-Ts9hQldoq221dmpJfrgG25lB0PBkjp4trfkleauGnds2MbDx9gEhvx8eL5_Q7nPjBTxUsAdhBFfBvlJvqda__vNRMwlUxXynHJ-PhMMXhC57vyTgVw25Uly2ODLbvZD2st6pMyxoXqmqh4-wbfC8bVH1iibHFFuhRtnqzkeCFQaRaz5sTnQHb2wfD3y-n1YoUn_x9gl0YfAPE6JMgz5afMP_ZxX1ofdw2uK6Qm6AHXzVf4TOsYVVIy-6sRXECQC7WkPC8Nvdr-vVIiCjrdwdjCr9IqVEv1DT4rJtQsz-aY-3sR1nRxMWHPDcNdoV2VOXBkYu3JAg-qruyWsiy45WCakCwBdIrZRA18tRm-T8YlmLjaw8ZVKr_ZTv9kteeI94M23awWIlgKlnm7L7ezmIQYw6HhKYxBB7EK-Nqa6lwDdUDgS6G09zsTLjxE7LWl52p9qvVOrx2qnfuCYM2eRKsCZmHyTK9ls6UiFjYwPWsKTN6cdeoGLCTlmKNjRGFZkdRZX0MuRmgQkmUZxWqlENdpj40qSbI7JTpyTdAwP33MX82CCLjifynZgWfME_hLoE9vyozzgLbuyfPIkFhOHSsZBc4DILkpBcOgiT37KlWXjvEVHm21Xel1bNR7tTTJuITEO8hI608BrmcYPTZsAFlGNnSw6KCvD4sbi49P2z0akWqMPiz_wtU2J45j2w-C8lWRRNfRn46cBJl4SUBIWQfTsi0Zv9AcJ1OZNtNHSAqIfNLwumfallcoImW1tqvj5Nn6N2VrUKBsCcttU1Kzbtf9sGfL49yEphbVvE_OgDsunGo_SXXGXJbna0kdY_vEvdUpfk5d5x9I4wET6XoRiZ4AE_DXaGokPLU8ldPuRYSVkAXg2edy4dB9C6QUVSV_mjXx4ZTuDhirnm8eEBrJjuKNuANWy6PAj1uKKMce5w_eDJzzlnFZwU3LNRpE9Jgip2XVipO6kycTV76jfj42hrVq9KTcILu0RamyiqEOloSgF1I0mm9JBRUeWkChSy0YJ49Hyge252ez2RW1u-G8zjPmOqf-RSP7UKvbAyw9q7X0Doh87sIn99Aiv5bnh5cTNl2d1pZXs1VotvdCVkZmKeTAieUOtM94pasKCk8ca0oo1-FMOri93R1U4cCxVWeXwHJx2DvqyIYKo2zE6KN0BKt0a7ZJVUO15RX-nec84236ZyuZDC_o1yDZMjF8tA-WLvOhrdYaEx4qcFKZwjvnQkD08HWskMzzpgrYe024C3G_PF2_iMAPllZ7oG5OfCzJKnze_PdGeGpK6KCBi2JWXGvE7hP8H3au_jrEdzjzTpi143tAhJ7JmusLHP9gYW_6hrzwY98sUeosg6T5iCFecUronNHVb05uGPh-BVsMjp7zCuZ44TmrMlIa4rd3kEdD4lqNeT5AdO8eE8JTbKE1WHed5hiJga3tgJrdnga-H7HFXt8Hmu0Za9qGrX21FL3IHnXWpHfu9VJz14q3sqFNwFIQRsDMzAvgmxgB66FPYzB7NWin80P1VZo-QZvD4Q-mqYWUPsCod7NBNqEruK5BZi5iBGjeTx3btmCe6V5FbTTnlhQzfLh4Rr4mxaFPRnLh_S_soE4IFGkDDuy5V-8FLarWnZTxl-O1mFmd0nHAI9ak8RlTR39ooDLVxAWvUmqhZ-G_7lSUm0dWgkOEqHfBU_n4uJxDaFeEsboiU79kOxOoWRQo0GwEifaVLb-4jB0pUU-OsD3DUMrfHZqvAVv_xdn9VGBunTcl0Zb6fl0HdxXRuXlBdLXIkwj17J-9JM-5CpaMz-0_Z8nQZ3z3GYlOKszl5LpVH5c4sf1fI9wMBEIhtOaX6LkFJNlvCN-imemZHR4nYGLETvoh28J7sdddBd62aZounMjgNRxduCJfCNHbv9BRviWIKeKeTb0be8QLU-5q9UjBDKfe3o8Gn_eiF3pT7XmDmGQ1Yuip4M8-nDWsgHoc7lo3AivyLfcImUOv01recKNYx_qETKAZuVIvRKRoXtY7S3VAQZUHPVGsFOAH5uvb6h6S2xIuONiK3733F4naiaj5dTv8Z5SJ9atv5fpx8f6ycgAlvsA.fZ93Zkyy6orWvwEnJGDD4yZd6OCkuQD7rxTNcT8C48s; better-auth.session_token=MkI9dsc3pxdQSZBX0svK0R8fXaRlfvLR.vyrnwrTogBxTaIUO49M3A1fjkAV7FA5Vhh1mSH1SIV4=; better-auth.session_data=eyJzZXNzaW9uIjp7InNlc3Npb24iOnsiZXhwaXJlc0F0IjoiMjAyNi0wNC0yMVQwNjo1NTowNy44NDNaIiwidG9rZW4iOiJNa0k5ZHNjM3B4ZFFTWkJYMHN2SzBSOGZYYVJsZnZMUiIsImNyZWF0ZWRBdCI6IjIwMjYtMDQtMTRUMDY6NTU6MDcuODQ0WiIsInVwZGF0ZWRBdCI6IjIwMjYtMDQtMTRUMDY6NTU6MDcuODQ0WiIsImlwQWRkcmVzcyI6IjEyNy4wLjAuMSIsInVzZXJBZ2VudCI6Ik1vemlsbGEvNS4wIChMaW51eDsgQW5kcm9pZCAxMDsgSykgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzE0Ni4wLjAuMCBNb2JpbGUgU2FmYXJpLzUzNy4zNiIsInVzZXJJZCI6InpvakdwZ2g5bkJKbHZkbHZZQ0dnd3RYZkhiek9NQ1UzIiwiaWQiOiJsSWhlM3ZYZjJhUXlmbGQ5SmIxMlRsUzIzeFhqQWNJeSJ9LCJ1c2VyIjp7Im5hbWUiOiJTZWJhc3RpYW4iLCJlbWFpbCI6InJ1ZmZAaWxsLmNvLmF0IiwiZW1haWxWZXJpZmllZCI6dHJ1ZSwiY3JlYXRlZEF0IjoiMjAyNi0wNC0xNFQwNjo1NTowNy44NDFaIiwidXBkYXRlZEF0IjoiMjAyNi0wNC0xNFQwNjo1NTowNy44NDFaIiwiaWQiOiJ6b2pHcGdoOW5CSmx2ZGx2WUNHZ3d0WGZIYnpPTUNVMyJ9LCJ1cGRhdGVkQXQiOjE3NzYxNDk3MDc4NDQsInZlcnNpb24iOiIxIn0sImV4cGlyZXNBdCI6MTc3NjE1NjkwNzg0NCwic2lnbmF0dXJlIjoiSUNlQ1ZEQ18xNTVqbzNqSnpBNTNGNzRpMUhkZTZHVkhYUzBHTkRvMnI5dyJ9', host: 'localhost:8081', 'user-agent': 'okhttp/4.12.0' } Error fetching access token: [APIError: Account not found] { status: 'BAD_REQUEST', body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' }, headers: {}, statusCode: 400 } signOut was called ```
Author
Owner

@dosubot[bot] commented on GitHub (Apr 14, 2026):

Thanks for the detailed logs — this is very helpful. The cookies are present and properly chunked (better-auth.account_data.0 and better-auth.account_data.1), and chunked account cookies are supported in v1.6.2 [1]. So the chunking itself isn't the issue.

The most likely culprit is how the headers are being passed to getAccessToken. Better Auth's internal context creation requires the host (or x-forwarded-host) header to be present for proper request parsing [2]. Your logs show the host header is localhost:8081, which is present — but the issue may be the header object type. If req.headers is a plain object (as Expo API routes sometimes provide) rather than a Web API Headers instance, cookie parsing can fail silently.

Try wrapping the headers explicitly:

const tokens = await auth.api.getAccessToken({
  body: { providerId: "keycloak" },
  headers: new Headers(req.headers),
});

If that doesn't work, you can verify cookie parsing is working by testing getAccountCookie directly:

import { getAccountCookie } from "better-auth/cookies";

// Create a minimal context to test cookie reading
const accountData = await getAccountCookie({
  headers: new Headers(req.headers),
  // pass your auth context
});
console.log("Account cookie parsed:", accountData);

If accountData comes back null even with proper Headers, the encrypted cookie may be failing decryption (e.g., if BETTER_AUTH_SECRET changed between when the cookie was set and now).

Also worth noting: the account cookie has a hardcoded 5-minute TTL [3] with no auto-refresh. If there's any delay between sign-in and the API call, the cookie may have expired. Can you confirm this API call happens immediately after sign-in?

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:4242068926 --> @dosubot[bot] commented on GitHub (Apr 14, 2026): <!-- Answer --> Thanks for the detailed logs — this is very helpful. The cookies **are** present and properly chunked (`better-auth.account_data.0` and `better-auth.account_data.1`), and chunked account cookies are supported in v1.6.2 [[1]](https://github.com/better-auth/better-auth/pull/6393). So the chunking itself isn't the issue. The most likely culprit is **how the headers are being passed to `getAccessToken`**. Better Auth's internal context creation requires the `host` (or `x-forwarded-host`) header to be present for proper request parsing [[2]](https://github.com/better-auth/better-auth/blob/92256a2d0d7a7111321a56614c683875e96c6ccd/packages/better-auth/src/api/to-auth-endpoints.ts#L71-L82). Your logs show the `host` header is `localhost:8081`, which is present — but the issue may be the **header object type**. If `req.headers` is a plain object (as Expo API routes sometimes provide) rather than a Web API `Headers` instance, cookie parsing can fail silently. Try wrapping the headers explicitly: ```typescript const tokens = await auth.api.getAccessToken({ body: { providerId: "keycloak" }, headers: new Headers(req.headers), }); ``` If that doesn't work, you can verify cookie parsing is working by testing `getAccountCookie` directly: ```typescript import { getAccountCookie } from "better-auth/cookies"; // Create a minimal context to test cookie reading const accountData = await getAccountCookie({ headers: new Headers(req.headers), // pass your auth context }); console.log("Account cookie parsed:", accountData); ``` If `accountData` comes back `null` even with proper `Headers`, the encrypted cookie may be failing decryption (e.g., if `BETTER_AUTH_SECRET` changed between when the cookie was set and now). Also worth noting: the account cookie has a **hardcoded 5-minute TTL** [[3]](https://github.com/better-auth/better-auth/blob/92256a2d0d7a7111321a56614c683875e96c6ccd/packages/better-auth/src/cookies/session-store.ts#L286) with no auto-refresh. If there's any delay between sign-in and the API call, the cookie may have expired. Can you confirm this API call happens immediately after sign-in? <!-- 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?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=ca795918-a6ac-41ba-812c-7f167a9bacba&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=ca795918-a6ac-41ba-812c-7f167a9bacba&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=ca795918-a6ac-41ba-812c-7f167a9bacba&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=ca795918-a6ac-41ba-812c-7f167a9bacba&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=ca795918-a6ac-41ba-812c-7f167a9bacba&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=ca795918-a6ac-41ba-812c-7f167a9bacba&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=ca795918-a6ac-41ba-812c-7f167a9bacba&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&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=Thanks%20for%20the%20detailed%20logs%20%E2%80%94%20this%20is%20very%20helpful.%20The%20cookies%20%2A%2Aare%2A%2A%20present%20and%20properly%20chunked%20%28%60better-auth.account_data.0%60%20and%20%60better-auth.account_data.1%60%29%2C%20and%20chunked%20account%20cookies%20are%20supported%20in%20v1.6.2%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/pull/6393%29.%20So%20the%20chunking%20itself%20isn%27t%20the%20issue.%0A%0AThe%20most%20likely%20culprit%20is%20%2A%2Ahow%20the%20headers%20are%20being%20passed%20to%20%60getAccessToken%60%2A%2A.%20Better%20Auth%27s%20internal%20context%20creation%20requires%20the%20%60host%60%20%28or%20%60x-forwarded-host%60%29%20header%20to%20be%20present%20for%20proper%20request%20parsing%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/92256a2d0d7a7111321a56614c683875e96c6ccd/packages/better-auth/src/api/to-auth-endpoints.ts%23L71-L82%29.%20Your%20logs%20show%20the%20%60host%60%20header%20is%20%60localhost%3A8081%60%2C%20which%20is%20present%20%E2%80%94%20but%20the%20issue%20may%20be%20the%20%2A%2Aheader%20object%20type%2A%2A.%20If%20%60req.headers%60%20is%20a%20plain%20object%20%28as%20Expo%20API%20routes%20sometimes%20provide%29%20rather%20than%20a%20Web%20API%20%60Headers%60%20instance%2C%20cookie%20parsing%20can%20fail%20silently.%0A%0ATry%20wrapping%20the%20headers%20explicitly%3A%0A%0A%60%60%60typescript%0Aconst%20tokens%20%3D%20await%20auth.api.getAccessToken%28%7B%0A%20%20body%3A%20%7B%20providerId%3A%20%22keycloak%22%20%7D%2C%0A%20%20headers%3A%20new%20Headers%28req.headers%29%2C%0A%7D%29%3B%0A%60%60%60%0A%0AIf%20that%20doesn%27t%20work%2C%20you%20can%20verify%20cookie%20parsing%20is%20working%20by%20testing%20%60getAccountCookie%60%20directly%3A%0A%0A%60%60%60typescript%0Aimport%20%7B%20getAccountCookie%20%7D%20from%20%22better-auth/cookies%22%3B%0A%0A//%20Create%20a%20minimal%20context%20to%20test%20cookie%20reading%0Aconst%20accountData%20%3D%20await%20getAccountCookie%28%7B%0A%20%20headers%3A%20new%20Headers%28req.headers%29%2C%0A%20%20//%20pass%20your%20auth%20context%0A%7D%29%3B%0Aconsole.log%28%22Account%20cookie%20parsed%3A%22%2C%20accountData%29%3B%0A%60%60%60%0A%0AIf%20%60accountData%60%20comes%20back%20%60null%60%20even%20with%20proper%20%60Headers%60%2C%20the%20encrypted%20cookie%20may%20be%20failing%20decryption%20%28e.g.%2C%20if%20%60BETTER_AUTH_SECRET%60%20changed%20between%20when%20the%20cookie%20was%20set%20and%20now%29.%0A%0AAlso%20worth%20noting%3A%20the%20account%20cookie%20has%20a%20%2A%2Ahardcoded%205-minute%20TTL%2A%2A%20%5B%5B3%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/92256a2d0d7a7111321a56614c683875e96c6ccd/packages/better-auth/src/cookies/session-store.ts%23L286%29%20with%20no%20auto-refresh.%20If%20there%27s%20any%20delay%20between%20sign-in%20and%20the%20API%20call%2C%20the%20cookie%20may%20have%20expired.%20Can%20you%20confirm%20this%20API%20call%20happens%20immediately%20after%20sign-in%3F)&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/9151)
Author
Owner

@ruff-exec commented on GitHub (Apr 14, 2026):

@dosu

That does not fix the issue. And your second approach I cannot test, as I don't have the auth context in my expo api route. Where should I get it from?

<!-- gh-comment-id:4243319142 --> @ruff-exec commented on GitHub (Apr 14, 2026): @dosu That does not fix the issue. And your second approach I cannot test, as I don't have the auth context in my expo api route. Where should I get it from?
Author
Owner

@dosubot[bot] commented on GitHub (Apr 14, 2026):

I owe you a correction on my earlier suggestions — the cookies are clearly present and chunked correctly, so this isn't a header formatting issue. Let me trace what's actually happening.

Since you're getting ACCOUNT_NOT_FOUND (not UNAUTHORIZED), the session is resolving fine — the error occurs in the account matching step afterward. The getAccessToken endpoint reads the decrypted account cookie and then checks [1]:

if (
  accountData &&
  accountData.userId === resolvedUserId &&
  providerId === accountData.providerId &&  // ← this check
  (!accountId || accountData.accountId === accountId)
) {
  account = accountData;
}

If any of those conditions fail, it falls back to a database query — which returns nothing in pure stateless mode. The most likely failure point is providerId === accountData.providerId: there's a documented bug where updateAccountOnSignIn: true (which is the default) causes the account cookie to lose the providerId field after OAuth sign-in in stateless mode.

Try adding this to your config and re-authenticating:

account: {
  storeStateStrategy: 'cookie',
  storeAccountCookie: true,
  updateAccountOnSignIn: false,  // prevents providerId from being stripped
},

Regarding getAccountCookie — you're right that you can't easily call it from an Expo API route without the auth context. You'd need to import your auth instance and use it via middleware or a handler that provides the GenericEndpointContext. For a standalone API route, you're doing the right thing by calling auth.api.getAccessToken() with the request headers — that should work once the underlying cookie data is correct.

If setting updateAccountOnSignIn: false doesn't resolve it, the next step would be to manually decode the account cookie to inspect what fields are actually stored. That would confirm whether providerId is missing or if something else is off.

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:4243378823 --> @dosubot[bot] commented on GitHub (Apr 14, 2026): <!-- Answer --> I owe you a correction on my earlier suggestions — the cookies are clearly present and chunked correctly, so this isn't a header formatting issue. Let me trace what's actually happening. Since you're getting `ACCOUNT_NOT_FOUND` (not `UNAUTHORIZED`), the **session is resolving fine** — the error occurs in the account matching step afterward. The `getAccessToken` endpoint reads the decrypted account cookie and then checks [[1]](https://github.com/better-auth/better-auth/blob/9a6d4759cd4451f0535d53f171bcfc8891c41db7/packages/better-auth/src/api/routes/account.ts#L523-L530): ```typescript if ( accountData && accountData.userId === resolvedUserId && providerId === accountData.providerId && // ← this check (!accountId || accountData.accountId === accountId) ) { account = accountData; } ``` If any of those conditions fail, it falls back to a database query — which returns nothing in pure stateless mode. The most likely failure point is `providerId === accountData.providerId`: there's a [documented bug](https://github.com/better-auth/better-auth/issues/6770#issuecomment-3654710131) where `updateAccountOnSignIn: true` (which is the **default**) causes the account cookie to lose the `providerId` field after OAuth sign-in in stateless mode. **Try adding this to your config and re-authenticating:** ```typescript account: { storeStateStrategy: 'cookie', storeAccountCookie: true, updateAccountOnSignIn: false, // prevents providerId from being stripped }, ``` Regarding `getAccountCookie` — you're right that you can't easily call it from an Expo API route without the auth context. You'd need to import your `auth` instance and use it via middleware or a handler that provides the `GenericEndpointContext`. For a standalone API route, you're doing the right thing by calling `auth.api.getAccessToken()` with the request headers — that *should* work once the underlying cookie data is correct. If setting `updateAccountOnSignIn: false` doesn't resolve it, the next step would be to manually decode the account cookie to inspect what fields are actually stored. That would confirm whether `providerId` is missing or if something else is off. <!-- 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?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=50a83b79-dda2-452c-8b60-d0dbc3edd4e4&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=50a83b79-dda2-452c-8b60-d0dbc3edd4e4&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=50a83b79-dda2-452c-8b60-d0dbc3edd4e4&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=50a83b79-dda2-452c-8b60-d0dbc3edd4e4&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=50a83b79-dda2-452c-8b60-d0dbc3edd4e4&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=50a83b79-dda2-452c-8b60-d0dbc3edd4e4&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=50a83b79-dda2-452c-8b60-d0dbc3edd4e4&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&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=I%20owe%20you%20a%20correction%20on%20my%20earlier%20suggestions%20%E2%80%94%20the%20cookies%20are%20clearly%20present%20and%20chunked%20correctly%2C%20so%20this%20isn%27t%20a%20header%20formatting%20issue.%20Let%20me%20trace%20what%27s%20actually%20happening.%0A%0ASince%20you%27re%20getting%20%60ACCOUNT_NOT_FOUND%60%20%28not%20%60UNAUTHORIZED%60%29%2C%20the%20%2A%2Asession%20is%20resolving%20fine%2A%2A%20%E2%80%94%20the%20error%20occurs%20in%20the%20account%20matching%20step%20afterward.%20The%20%60getAccessToken%60%20endpoint%20reads%20the%20decrypted%20account%20cookie%20and%20then%20checks%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/9a6d4759cd4451f0535d53f171bcfc8891c41db7/packages/better-auth/src/api/routes/account.ts%23L523-L530%29%3A%0A%0A%60%60%60typescript%0Aif%20%28%0A%20%20accountData%20%26%26%0A%20%20accountData.userId%20%3D%3D%3D%20resolvedUserId%20%26%26%0A%20%20providerId%20%3D%3D%3D%20accountData.providerId%20%26%26%20%20//%20%E2%86%90%20this%20check%0A%20%20%28%21accountId%20%7C%7C%20accountData.accountId%20%3D%3D%3D%20accountId%29%0A%29%20%7B%0A%20%20account%20%3D%20accountData%3B%0A%7D%0A%60%60%60%0A%0AIf%20any%20of%20those%20conditions%20fail%2C%20it%20falls%20back%20to%20a%20database%20query%20%E2%80%94%20which%20returns%20nothing%20in%20pure%20stateless%20mode.%20The%20most%20likely%20failure%20point%20is%20%60providerId%20%3D%3D%3D%20accountData.providerId%60%3A%20there%27s%20a%20%5Bdocumented%20bug%5D%28https%3A//github.com/better-auth/better-auth/issues/6770%23issuecomment-3654710131%29%20where%20%60updateAccountOnSignIn%3A%20true%60%20%28which%20is%20the%20%2A%2Adefault%2A%2A%29%20causes%20the%20account%20cookie%20to%20lose%20the%20%60providerId%60%20field%20after%20OAuth%20sign-in%20in%20stateless%20mode.%0A%0A%2A%2ATry%20adding%20this%20to%20your%20config%20and%20re-authenticating%3A%2A%2A%0A%0A%60%60%60typescript%0Aaccount%3A%20%7B%0A%20%20storeStateStrategy%3A%20%27cookie%27%2C%0A%20%20storeAccountCookie%3A%20true%2C%0A%20%20updateAccountOnSignIn%3A%20false%2C%20%20//%20prevents%20providerId%20from%20being%20stripped%0A%7D%2C%0A%60%60%60%0A%0ARegarding%20%60getAccountCookie%60%20%E2%80%94%20you%27re%20right%20that%20you%20can%27t%20easily%20call%20it%20from%20an%20Expo%20API%20route%20without%20the%20auth%20context.%20You%27d%20need%20to%20import%20your%20%60auth%60%20instance%20and%20use%20it%20via%20middleware%20or%20a%20handler%20that%20provides%20the%20%60GenericEndpointContext%60.%20For%20a%20standalone%20API%20route%2C%20you%27re%20doing%20the%20right%20thing%20by%20calling%20%60auth.api.getAccessToken%28%29%60%20with%20the%20request%20headers%20%E2%80%94%20that%20%2Ashould%2A%20work%20once%20the%20underlying%20cookie%20data%20is%20correct.%0A%0AIf%20setting%20%60updateAccountOnSignIn%3A%20false%60%20doesn%27t%20resolve%20it%2C%20the%20next%20step%20would%20be%20to%20manually%20decode%20the%20account%20cookie%20to%20inspect%20what%20fields%20are%20actually%20stored.%20That%20would%20confirm%20whether%20%60providerId%60%20is%20missing%20or%20if%20something%20else%20is%20off.)&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/9151)
Author
Owner

@SferaDev commented on GitHub (Apr 15, 2026):

We are also seeing sporadic ACCOUNT_NOT_FOUND after updating to 1.6.x with keycloak

<!-- gh-comment-id:4253032535 --> @SferaDev commented on GitHub (Apr 15, 2026): We are also seeing sporadic `ACCOUNT_NOT_FOUND` after updating to `1.6.x` with keycloak
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28612