[GH-ISSUE #7491] [BUG] [Expo] Importing expoClient crashes on latest release: fromJSONSchema undefined (regression since f4a9f15) #28147

Closed
opened 2026-04-17 19:33:21 -05:00 by GiteaMirror · 8 comments
Owner

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

Originally assigned to: @bytaesu on GitHub.

Summary

After merging PR #6933 (cookie-based OAuth state fix for Expo), upgrading from a working continuous build to newer builds appears to introduce a new regression: importing expoClient crashes at runtime with fromJSONSchema being undefined.

Regression window

  • Works on continuous release at commit f4a9f15
  • Breaks on the next continuous release(s) containing the 3 subsequent commits after f4a9f15, and also on the latest stable release

I’m not sure which of the 3 commits after f4a9f15 introduced this, but one of them seems to.

Steps to reproduce

  1. Use the continuous release at commit f4a9f15 → app works.
  2. Upgrade to either:
    • the next continuous release (the 3 commits after f4a9f15), or
    • the latest stable release
  3. Run the app (Expo / React Native).
  4. The app crashes as soon as expoClient is imported.

Expected behavior

Importing expoClient from @better-auth/expo/client should work without crashing.

Actual behavior

Importing expoClient throws:

ERROR  [TypeError: Cannot read property 'fromJSONSchema' of undefined]

Stack trace / context:

Code: auth-client.ts
  2 | import { genericOAuthClient } from 'better-auth/client/plugins'
  3 | import { createAuthClient } from 'better-auth/react'
> 4 | import { expoClient } from '@better-auth/expo/client'
    | ^
  5 |
  6 | export const authClient = createAuthClient({
  7 |   baseURL: 'http://localhost:8081',
Call Stack
  <global> (src/client/auth/auth-client.ts:4)
  <global> (src/app/(authenticated)/index.tsx:4)

Environment

What version of Better Auth are you using?

v1.4.16

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": "6.78 GB"
  },
  "node": {
    "version": "v22.12.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.7.0"
  },
  "frameworks": [
    {
      "name": "react",
      "version": "19.1.4"
    }
  ],
  "databases": null,
  "betterAuth": {
    "version": "1.4.7",
    "config": {
      "plugins": [
        {
          "name": "expo",
          "config": {
            "id": "expo",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {}
          }
        },
        {
          "name": "generic-oauth",
          "config": {
            "id": "generic-oauth",
            "endpoints": {},
            "$ERROR_CODES": {
              "INVALID_OAUTH_CONFIGURATION": "Invalid OAuth configuration",
              "TOKEN_URL_NOT_FOUND": "Invalid OAuth configuration. Token URL not found.",
              "PROVIDER_CONFIG_NOT_FOUND": "No config found for provider",
              "PROVIDER_ID_REQUIRED": "Provider ID is required",
              "INVALID_OAUTH_CONFIG": "Invalid OAuth configuration.",
              "SESSION_REQUIRED": "Session is required"
            }
          }
        }
      ],
      "trustedOrigins": [
        "com.company.app://"
      ],
      "session": {
        "cookieCache": {
          "enabled": true,
          "maxAge": 7200,
          "strategy": "jwt",
          "refreshCache": true
        }
      },
      "account": {
        "storeStateStrategy": "cookie",
        "storeAccountCookie": true,
        "skipStateCookieCheck": true
      }
    }
  }
}

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

Backend, Client

Auth config (if applicable)

import 'server-only'
import { betterAuth } from 'better-auth'
import { genericOAuth, keycloak } from 'better-auth/plugins'
import { expo } from '@better-auth/expo'

export const auth = betterAuth({
  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,
        }),
      ],
    }),
  ],
  trustedOrigins: [
    'com.company.app://',
    // 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: 'jwt',
      refreshCache: true,
    },
  },
  account: {
    storeStateStrategy: 'cookie',
    storeAccountCookie: true,
    skipStateCookieCheck: true,
  },
})

Related

Originally created by @ruff-exec on GitHub (Jan 20, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7491 Originally assigned to: @bytaesu on GitHub. # Summary After merging PR **#6933** (cookie-based OAuth state fix for Expo), upgrading from a working continuous build to newer builds appears to introduce a new regression: importing `expoClient` crashes at runtime with `fromJSONSchema` being `undefined`. # Regression window - ✅ **Works on** continuous release at commit [`f4a9f15`](https://github.com/better-auth/better-auth/pull/6933/commits/f4a9f15c3f446f971bc34a51fdb42fbcde4dd2b6) - ❌ **Breaks on** the **next continuous release(s)** containing the **3 subsequent commits** after `f4a9f15`, and also on the **latest stable release** I’m not sure which of the 3 commits after `f4a9f15` introduced this, but one of them seems to. # Steps to reproduce 1. Use the continuous release at commit `f4a9f15` → app works. 2. Upgrade to either: - the next continuous release (the 3 commits after `f4a9f15`), **or** - the latest stable release 3. Run the app (Expo / React Native). 4. The app crashes as soon as `expoClient` is imported. # Expected behavior Importing `expoClient` from `@better-auth/expo/client` should work without crashing. # Actual behavior Importing `expoClient` throws: ```txt ERROR [TypeError: Cannot read property 'fromJSONSchema' of undefined] ``` Stack trace / context: ```ts Code: auth-client.ts 2 | import { genericOAuthClient } from 'better-auth/client/plugins' 3 | import { createAuthClient } from 'better-auth/react' > 4 | import { expoClient } from '@better-auth/expo/client' | ^ 5 | 6 | export const authClient = createAuthClient({ 7 | baseURL: 'http://localhost:8081', Call Stack <global> (src/client/auth/auth-client.ts:4) <global> (src/app/(authenticated)/index.tsx:4) ``` # Environment ### What version of Better Auth are you using? v1.4.16 ### 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": "6.78 GB" }, "node": { "version": "v22.12.0", "env": "development" }, "packageManager": { "name": "npm", "version": "11.7.0" }, "frameworks": [ { "name": "react", "version": "19.1.4" } ], "databases": null, "betterAuth": { "version": "1.4.7", "config": { "plugins": [ { "name": "expo", "config": { "id": "expo", "hooks": { "after": [ {} ] }, "endpoints": {} } }, { "name": "generic-oauth", "config": { "id": "generic-oauth", "endpoints": {}, "$ERROR_CODES": { "INVALID_OAUTH_CONFIGURATION": "Invalid OAuth configuration", "TOKEN_URL_NOT_FOUND": "Invalid OAuth configuration. Token URL not found.", "PROVIDER_CONFIG_NOT_FOUND": "No config found for provider", "PROVIDER_ID_REQUIRED": "Provider ID is required", "INVALID_OAUTH_CONFIG": "Invalid OAuth configuration.", "SESSION_REQUIRED": "Session is required" } } } ], "trustedOrigins": [ "com.company.app://" ], "session": { "cookieCache": { "enabled": true, "maxAge": 7200, "strategy": "jwt", "refreshCache": true } }, "account": { "storeStateStrategy": "cookie", "storeAccountCookie": true, "skipStateCookieCheck": true } } } } ``` ### Which area(s) are affected? (Select all that apply) Backend, Client ### Auth config (if applicable) ```typescript import 'server-only' import { betterAuth } from 'better-auth' import { genericOAuth, keycloak } from 'better-auth/plugins' import { expo } from '@better-auth/expo' export const auth = betterAuth({ 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, }), ], }), ], trustedOrigins: [ 'com.company.app://', // 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: 'jwt', refreshCache: true, }, }, account: { storeStateStrategy: 'cookie', storeAccountCookie: true, skipStateCookieCheck: true, }, }) ``` # Related - PR that may have introduced the regression: **#6933** - Original discussion: _Originally posted by @ruff-exec in_ https://github.com/better-auth/better-auth/issues/6933#issuecomment-3767063608
GiteaMirror added the lockedbug labels 2026-04-17 19:33:21 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Jan 20, 2026):

It seems you're encountering a regression issue when importing expoClient from @better-auth/expo/client. This has been recorded as a problem since the continuous release after commit f4a9f15, leading to a crash due to fromJSONSchema being undefined [1].

Currently, there hasn't been a definitive fix issued for this specific regression, but it is best to keep an eye out for updates or potential patches from the Better Auth team in future releases. In the meantime, consider this step:

  1. Revert to the last known stable commit (i.e., f4a9f15) where your app worked correctly.
  2. Monitor the GitHub repository for resolutions or patches, specifically on issues tagged as regression or similar concerning Expo.

If you need immediate assistance or want more insights, please tag @bekacru or @ping__ for detailed support!

Is there a specific implementation or feature you're currently working on with better-auth that you would like more information about?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3771812550 --> @better-auth-agent[bot] commented on GitHub (Jan 20, 2026): It seems you're encountering a regression issue when importing `expoClient` from `@better-auth/expo/client`. This has been recorded as a problem since the continuous release after commit `f4a9f15`, leading to a crash due to `fromJSONSchema` being `undefined` [[1]](https://github.com/better-auth/better-auth/issues/2682). Currently, there hasn't been a definitive fix issued for this specific regression, but it is best to keep an eye out for updates or potential patches from the Better Auth team in future releases. In the meantime, consider this step: 1. **Revert** to the last known stable commit (i.e., `f4a9f15`) where your app worked correctly. 2. **Monitor** the GitHub repository for resolutions or patches, specifically on issues tagged as regression or similar concerning Expo. If you need immediate assistance or want more insights, please tag @bekacru or @ping__ for detailed support! Is there a specific implementation or feature you're currently working on with better-auth that you would like more information about? _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@dosubot[bot] commented on GitHub (Jan 20, 2026):

This regression is caused by the switch to ESM-only modules in Better Auth after commit f4a9f15. The package now only ships .mjs files (no .cjs), but Metro bundler on iOS tries to resolve @better-auth/expo/dist/client.cjs, which doesn't exist. This leads to runtime errors like fromJSONSchema being undefined, even though that symbol isn't referenced in the source code—the error is a symptom of a failed/broken import, not a missing function in expoClient itself [source].

Workarounds:

  1. In your babel.config.js, use babel-plugin-module-resolver to alias @better-auth/expo/client to the .mjs file:
    module.exports = function (api) {
      api.cache(true);
      return {
        presets: ["babel-preset-expo"],
        plugins: [
          [
            "module-resolver",
            {
              alias: {
                "@better-auth/expo/client": "./node_modules/@better-auth/expo/dist/client.mjs",
                "better-auth/react": "./node_modules/better-auth/dist/client/react/index.mjs",
                "better-auth/client/plugins": "./node_modules/better-auth/dist/client/plugins/index.mjs",
              },
            },
          ],
        ],
      };
    };
    
  2. In your metro.config.js, enable package exports and set the condition names:
    const { getDefaultConfig } = require("expo/metro-config");
    
    const config = getDefaultConfig(__dirname);
    config.resolver.unstable_enablePackageExports = true;
    config.resolver.unstable_conditionNames = [
      "react-native",
      "import",
      "require",
      "browser",
      "types",
    ];
    
    module.exports = config;
    
  3. Clear Metro's cache and rebuild your dev client.

Caveats:
Some users report that even with these workarounds, iOS Metro may still try to resolve client.cjs and fail. This appears to be a deeper issue with Metro's ESM resolution logic on iOS, sometimes bypassing Babel aliases or expecting a CJS fallback [source]. Android usually works because its Metro resolver is less strict.

References:

If you still see the error after these changes, keep an eye on the linked issue for updates or fixes as Metro and Better Auth continue to improve ESM support.

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:3771839846 --> @dosubot[bot] commented on GitHub (Jan 20, 2026): <!-- Answer --> This regression is caused by the switch to ESM-only modules in Better Auth after commit `f4a9f15`. The package now only ships `.mjs` files (no `.cjs`), but Metro bundler on iOS tries to resolve `@better-auth/expo/dist/client.cjs`, which doesn't exist. This leads to runtime errors like `fromJSONSchema` being undefined, even though that symbol isn't referenced in the source code—the error is a symptom of a failed/broken import, not a missing function in expoClient itself [[source](https://github.com/better-auth/better-auth/issues/7218)]. **Workarounds:** 1. In your `babel.config.js`, use `babel-plugin-module-resolver` to alias `@better-auth/expo/client` to the `.mjs` file: ```js module.exports = function (api) { api.cache(true); return { presets: ["babel-preset-expo"], plugins: [ [ "module-resolver", { alias: { "@better-auth/expo/client": "./node_modules/@better-auth/expo/dist/client.mjs", "better-auth/react": "./node_modules/better-auth/dist/client/react/index.mjs", "better-auth/client/plugins": "./node_modules/better-auth/dist/client/plugins/index.mjs", }, }, ], ], }; }; ``` 2. In your `metro.config.js`, enable package exports and set the condition names: ```js const { getDefaultConfig } = require("expo/metro-config"); const config = getDefaultConfig(__dirname); config.resolver.unstable_enablePackageExports = true; config.resolver.unstable_conditionNames = [ "react-native", "import", "require", "browser", "types", ]; module.exports = config; ``` 3. Clear Metro's cache and rebuild your dev client. **Caveats:** Some users report that even with these workarounds, iOS Metro may still try to resolve `client.cjs` and fail. This appears to be a deeper issue with Metro's ESM resolution logic on iOS, sometimes bypassing Babel aliases or expecting a CJS fallback [[source](https://github.com/better-auth/better-auth/issues/7218#issuecomment-3727739125)]. Android usually works because its Metro resolver is less strict. **References:** - [Expo ESM/CJS Metro issue and workarounds](https://github.com/better-auth/better-auth/issues/7218) - [Better Auth ESM-only migration PR](https://github.com/better-auth/better-auth/pull/5703) - [Expo babel-config guide update](https://github.com/better-auth/better-auth/pull/7254) If you still see the error after these changes, keep an eye on the linked issue for updates or fixes as Metro and Better Auth continue to improve ESM support. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/9aaf825b-f4cd-45db-a1bc-64c44140fec5?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/9aaf825b-f4cd-45db-a1bc-64c44140fec5?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/9aaf825b-f4cd-45db-a1bc-64c44140fec5?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/9aaf825b-f4cd-45db-a1bc-64c44140fec5?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/9aaf825b-f4cd-45db-a1bc-64c44140fec5?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/9aaf825b-f4cd-45db-a1bc-64c44140fec5?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/9aaf825b-f4cd-45db-a1bc-64c44140fec5?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20regression%20is%20caused%20by%20the%20switch%20to%20ESM-only%20modules%20in%20Better%20Auth%20after%20commit%20%60f4a9f15%60.%20The%20package%20now%20only%20ships%20%60.mjs%60%20files%20%28no%20%60.cjs%60%29%2C%20but%20Metro%20bundler%20on%20iOS%20tries%20to%20resolve%20%60%40better-auth/expo/dist/client.cjs%60%2C%20which%20doesn%27t%20exist.%20This%20leads%20to%20runtime%20errors%20like%20%60fromJSONSchema%60%20being%20undefined%2C%20even%20though%20that%20symbol%20isn%27t%20referenced%20in%20the%20source%20code%E2%80%94the%20error%20is%20a%20symptom%20of%20a%20failed/broken%20import%2C%20not%20a%20missing%20function%20in%20expoClient%20itself%20%5B%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/7218%29%5D.%0A%0A%2A%2AWorkarounds%3A%2A%2A%0A%0A1.%20In%20your%20%60babel.config.js%60%2C%20use%20%60babel-plugin-module-resolver%60%20to%20alias%20%60%40better-auth/expo/client%60%20to%20the%20%60.mjs%60%20file%3A%0A%20%20%20%60%60%60js%0A%20%20%20module.exports%20%3D%20function%20%28api%29%20%7B%0A%20%20%20%20%20api.cache%28true%29%3B%0A%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20presets%3A%20%5B%22babel-preset-expo%22%5D%2C%0A%20%20%20%20%20%20%20plugins%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%22module-resolver%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20alias%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%40better-auth/expo/client%22%3A%20%22./node_modules/%40better-auth/expo/dist/client.mjs%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22better-auth/react%22%3A%20%22./node_modules/better-auth/dist/client/react/index.mjs%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22better-auth/client/plugins%22%3A%20%22./node_modules/better-auth/dist/client/plugins/index.mjs%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%7D%3B%0A%20%20%20%7D%3B%0A%20%20%20%60%60%60%0A2.%20In%20your%20%60metro.config.js%60%2C%20enable%20package%20exports%20and%20set%20the%20condition%20names%3A%0A%20%20%20%60%60%60js%0A%20%20%20const%20%7B%20getDefaultConfig%20%7D%20%3D%20require%28%22expo/metro-config%22%29%3B%0A%0A%20%20%20const%20config%20%3D%20getDefaultConfig%28__dirname%29%3B%0A%20%20%20config.resolver.unstable_enablePackageExports%20%3D%20true%3B%0A%20%20%20config.resolver.unstable_conditionNames%20%3D%20%5B%0A%20%20%20%20%20%22react-native%22%2C%0A%20%20%20%20%20%22import%22%2C%0A%20%20%20%20%20%22require%22%2C%0A%20%20%20%20%20%22browser%22%2C%0A%20%20%20%20%20%22types%22%2C%0A%20%20%20%5D%3B%0A%0A%20%20%20module.exports%20%3D%20config%3B%0A%20%20%20%60%60%60%0A3.%20Clear%20Metro%27s%20cache%20and%20rebuild%20your%20dev%20client.%0A%0A%2A%2ACaveats%3A%2A%2A%20%20%0ASome%20users%20report%20that%20even%20with%20these%20workarounds%2C%20iOS%20Metro%20may%20still%20try%20to%20resolve%20%60client.cjs%60%20and%20fail.%20This%20appears%20to%20be%20a%20deeper%20issue%20with%20Metro%27s%20ESM%20resolution%20logic%20on%20iOS%2C%20sometimes%20bypassing%20Babel%20aliases%20or%20expecting%20a%20CJS%20fallback%20%5B%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/7218%23issuecomment-3727739125%29%5D.%20Android%20usually%20works%20because%20its%20Metro%20resolver%20is%20less%20strict.%0A%0A%2A%2AReferences%3A%2A%2A%20%20%0A-%20%5BExpo%20ESM/CJS%20Metro%20issue%20and%20workarounds%5D%28https%3A//github.com/better-auth/better-auth/issues/7218%29%0A-%20%5BBetter%20Auth%20ESM-only%20migration%20PR%5D%28https%3A//github.com/better-auth/better-auth/pull/5703%29%0A-%20%5BExpo%20babel-config%20guide%20update%5D%28https%3A//github.com/better-auth/better-auth/pull/7254%29%0A%0AIf%20you%20still%20see%20the%20error%20after%20these%20changes%2C%20keep%20an%20eye%20on%20the%20linked%20issue%20for%20updates%20or%20fixes%20as%20Metro%20and%20Better%20Auth%20continue%20to%20improve%20ESM%20support.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7491)
Author
Owner

@ruff-exec commented on GitHub (Jan 20, 2026):

@dosu Thanks! I re-tested and I don’t think this is a “wrong branch / not merged” situation.

  • Works on the continuous build at commit f4a9f15 (linked in the issue)
  • Breaks on the next continuous build that includes the commits after f4a9f15
  • Also breaks on the latest stable release

So the regression is after the merge point that contains the working commit.

And I use Android to test where I get this issue not iOS

<!-- gh-comment-id:3772132834 --> @ruff-exec commented on GitHub (Jan 20, 2026): @dosu Thanks! I re-tested and I don’t think this is a “wrong branch / not merged” situation. - ✅ Works on the continuous build at commit f4a9f15 (linked in the issue) - ❌ Breaks on the *next* continuous build that includes the commits after f4a9f15 - ❌ Also breaks on the latest stable release So the regression is *after* the merge point that contains the working commit. And I use Android to test where I get this issue not iOS
Author
Owner

@dosubot[bot] commented on GitHub (Jan 20, 2026):

You're right—the regression is not a branch issue, but is caused by the ESM-only migration right after f4a9f15. After that point, all Better Auth packages (including @better-auth/expo) only ship .mjs files and no longer provide .cjs (CommonJS) builds. This breaks Metro's module resolution if it tries to load a .cjs file, which can happen on both Android and iOS, depending on your project setup and Metro version. The fromJSONSchema error is a symptom of this failed import, not a missing export in the code itself [source].

To fix this on Android, make sure you:

  1. In babel.config.js, alias @better-auth/expo/client to the .mjs file:
    module.exports = function (api) {
      api.cache(true);
      return {
        presets: ["babel-preset-expo"],
        plugins: [
          [
            "module-resolver",
            {
              alias: {
                "@better-auth/expo/client": "./node_modules/@better-auth/expo/dist/client.mjs",
              },
            },
          ],
        ],
      };
    };
    
  2. In metro.config.js, enable package exports and set the condition names:
    const { getDefaultConfig } = require("expo/metro-config");
    
    const config = getDefaultConfig(__dirname);
    config.resolver.unstable_enablePackageExports = true;
    config.resolver.unstable_conditionNames = [
      "react-native",
      "import",
      "require",
      "browser",
      "types",
    ];
    
    module.exports = config;
    
  3. Clear Metro's cache and rebuild your dev client.

If you use other Better Auth plugins, make sure none of them depend on Node.js built-ins (like crypto or buffer) unless you polyfill them (e.g., with expo-crypto or expo-standard-web-crypto). If you still see the error after these changes, try isolating the import in a fresh Expo project to rule out project-specific config issues.

This is a known issue and is being tracked in the repo [source]. If the workaround doesn't resolve it, let me know what error you see after making these changes.

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:3772154190 --> @dosubot[bot] commented on GitHub (Jan 20, 2026): <!-- Answer --> You're right—the regression is not a branch issue, but is caused by the ESM-only migration right after f4a9f15. After that point, all Better Auth packages (including @better-auth/expo) only ship `.mjs` files and no longer provide `.cjs` (CommonJS) builds. This breaks Metro's module resolution if it tries to load a `.cjs` file, which can happen on both Android and iOS, depending on your project setup and Metro version. The `fromJSONSchema` error is a symptom of this failed import, not a missing export in the code itself [[source](https://github.com/better-auth/better-auth/pull/5703)]. To fix this on Android, make sure you: 1. In `babel.config.js`, alias `@better-auth/expo/client` to the `.mjs` file: ```js module.exports = function (api) { api.cache(true); return { presets: ["babel-preset-expo"], plugins: [ [ "module-resolver", { alias: { "@better-auth/expo/client": "./node_modules/@better-auth/expo/dist/client.mjs", }, }, ], ], }; }; ``` 2. In `metro.config.js`, enable package exports and set the condition names: ```js const { getDefaultConfig } = require("expo/metro-config"); const config = getDefaultConfig(__dirname); config.resolver.unstable_enablePackageExports = true; config.resolver.unstable_conditionNames = [ "react-native", "import", "require", "browser", "types", ]; module.exports = config; ``` 3. Clear Metro's cache and rebuild your dev client. If you use other Better Auth plugins, make sure none of them depend on Node.js built-ins (like `crypto` or `buffer`) unless you polyfill them (e.g., with `expo-crypto` or `expo-standard-web-crypto`). If you still see the error after these changes, try isolating the import in a fresh Expo project to rule out project-specific config issues. This is a known issue and is being tracked in the repo [[source](https://github.com/better-auth/better-auth/issues/7218)]. If the workaround doesn't resolve it, let me know what error you see after making these changes. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/f907faaa-98d9-4370-92b6-d2cb1ea7bca9?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/f907faaa-98d9-4370-92b6-d2cb1ea7bca9?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/f907faaa-98d9-4370-92b6-d2cb1ea7bca9?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/f907faaa-98d9-4370-92b6-d2cb1ea7bca9?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/f907faaa-98d9-4370-92b6-d2cb1ea7bca9?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/f907faaa-98d9-4370-92b6-d2cb1ea7bca9?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/f907faaa-98d9-4370-92b6-d2cb1ea7bca9?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=You%27re%20right%E2%80%94the%20regression%20is%20not%20a%20branch%20issue%2C%20but%20is%20caused%20by%20the%20ESM-only%20migration%20right%20after%20f4a9f15.%20After%20that%20point%2C%20all%20Better%20Auth%20packages%20%28including%20%40better-auth/expo%29%20only%20ship%20%60.mjs%60%20files%20and%20no%20longer%20provide%20%60.cjs%60%20%28CommonJS%29%20builds.%20This%20breaks%20Metro%27s%20module%20resolution%20if%20it%20tries%20to%20load%20a%20%60.cjs%60%20file%2C%20which%20can%20happen%20on%20both%20Android%20and%20iOS%2C%20depending%20on%20your%20project%20setup%20and%20Metro%20version.%20The%20%60fromJSONSchema%60%20error%20is%20a%20symptom%20of%20this%20failed%20import%2C%20not%20a%20missing%20export%20in%20the%20code%20itself%20%5B%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/pull/5703%29%5D.%0A%0ATo%20fix%20this%20on%20Android%2C%20make%20sure%20you%3A%0A%0A1.%20In%20%60babel.config.js%60%2C%20alias%20%60%40better-auth/expo/client%60%20to%20the%20%60.mjs%60%20file%3A%0A%20%20%20%60%60%60js%0A%20%20%20module.exports%20%3D%20function%20%28api%29%20%7B%0A%20%20%20%20%20api.cache%28true%29%3B%0A%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20presets%3A%20%5B%22babel-preset-expo%22%5D%2C%0A%20%20%20%20%20%20%20plugins%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%22module-resolver%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20alias%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%40better-auth/expo/client%22%3A%20%22./node_modules/%40better-auth/expo/dist/client.mjs%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%7D%3B%0A%20%20%20%7D%3B%0A%20%20%20%60%60%60%0A2.%20In%20%60metro.config.js%60%2C%20enable%20package%20exports%20and%20set%20the%20condition%20names%3A%0A%20%20%20%60%60%60js%0A%20%20%20const%20%7B%20getDefaultConfig%20%7D%20%3D%20require%28%22expo/metro-config%22%29%3B%0A%0A%20%20%20const%20config%20%3D%20getDefaultConfig%28__dirname%29%3B%0A%20%20%20config.resolver.unstable_enablePackageExports%20%3D%20true%3B%0A%20%20%20config.resolver.unstable_conditionNames%20%3D%20%5B%0A%20%20%20%20%20%22react-native%22%2C%0A%20%20%20%20%20%22import%22%2C%0A%20%20%20%20%20%22require%22%2C%0A%20%20%20%20%20%22browser%22%2C%0A%20%20%20%20%20%22types%22%2C%0A%20%20%20%5D%3B%0A%0A%20%20%20module.exports%20%3D%20config%3B%0A%20%20%20%60%60%60%0A3.%20Clear%20Metro%27s%20cache%20and%20rebuild%20your%20dev%20client.%0A%0AIf%20you%20use%20other%20Better%20Auth%20plugins%2C%20make%20sure%20none%20of%20them%20depend%20on%20Node.js%20built-ins%20%28like%20%60crypto%60%20or%20%60buffer%60%29%20unless%20you%20polyfill%20them%20%28e.g.%2C%20with%20%60expo-crypto%60%20or%20%60expo-standard-web-crypto%60%29.%20If%20you%20still%20see%20the%20error%20after%20these%20changes%2C%20try%20isolating%20the%20import%20in%20a%20fresh%20Expo%20project%20to%20rule%20out%20project-specific%20config%20issues.%0A%0AThis%20is%20a%20known%20issue%20and%20is%20being%20tracked%20in%20the%20repo%20%5B%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/7218%29%5D.%20If%20the%20workaround%20doesn%27t%20resolve%20it%2C%20let%20me%20know%20what%20error%20you%20see%20after%20making%20these%20changes.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7491)
Author
Owner

@bytaesu commented on GitHub (Jan 20, 2026):

Hi @ruff-exec,

It looks like an issue came up after I refactored to use a centralized utility. I’ll take a look 🙏

<!-- gh-comment-id:3773936776 --> @bytaesu commented on GitHub (Jan 20, 2026): Hi @ruff-exec, It looks like an issue came up after I refactored to use a centralized utility. I’ll take a look 🙏
Author
Owner

@bytaesu commented on GitHub (Jan 20, 2026):

Hi @ruff-exec,

Following our docs, I set up a very minimal app and didn't run into issues on my end. Could you let me know if you’ve configured Metro Bundler?

https://www.better-auth.com/docs/integrations/expo#configure-metro-bundler


Image
<!-- gh-comment-id:3774032787 --> @bytaesu commented on GitHub (Jan 20, 2026): Hi @ruff-exec, Following our docs, I set up a very minimal app and didn't run into issues on my end. Could you let me know if you’ve configured Metro Bundler? https://www.better-auth.com/docs/integrations/expo#configure-metro-bundler --- <img height="300" alt="Image" src="https://github.com/user-attachments/assets/85df4047-6d9e-450d-86a7-3db855fc47df" />
Author
Owner

@himself65 commented on GitHub (Jan 20, 2026):

Upstream issue https://github.com/colinhacks/zod/issues/5536

<!-- gh-comment-id:3775423144 --> @himself65 commented on GitHub (Jan 20, 2026): Upstream issue https://github.com/colinhacks/zod/issues/5536
Author
Owner

@ashuvssut commented on GitHub (Feb 20, 2026):

The Docs BA Bot proposed this solution to me by referencing this GitHub issue

BA Bot proposed solution:-

The error persists because Metro bundler still cannot resolve the internal @better-auth/core/utils dependency from the @better-auth/expo/client package. This is a known Metro bundling issue that affects both iOS and Android platforms.

Since the basic workaround didn't resolve the issue, try this more comprehensive configuration:

1. Update your babel.config.js with all Better Auth aliases:

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
    plugins: [
      [
        "module-resolver",
        {
          alias: {
            "better-auth/react": "./node_modules/better-auth/dist/client/react/index.mjs",
            "better-auth/client/plugins": "./node_modules/better-auth/dist/client/plugins/index.mjs",
            "@better-auth/expo/client": "./node_modules/@better-auth/expo/dist/client.mjs",
          },
        },
      ],
    ],
  };
};

2. Update your metro.config.js with the complete configuration:

const { getDefaultConfig } = require("expo/metro-config");

const config = getDefaultConfig(__dirname);
config.resolver.unstable_enablePackageExports = true;
config.resolver.unstable_conditionNames = [
  "react-native",
  "import",
  "require",
  "browser",
  "types",
];

module.exports = config;

3. Clear Metro's cache and rebuild your dev client:

npx expo start --clear

The issue is caused by the ESM-only migration in Better Auth where packages only ship .mjs files and Metro's internal resolution logic sometimes bypasses Babel aliases or expects a CJS fallback that doesn't exist. This is a deeper Metro bundler issue with ESM resolution that can affect both Android and iOS depending on your project setup.

If you continue experiencing the error after these changes, you may need to wait for an official fix as this is being tracked as a known issue.

None of the above solutions worked for me. Its maybe because I am using a MONOREPO SETUP. For babel module-transformer, I adjusted the node_modules path to point to the correct dist/client.mjs path, but it introduced a lot of other issues like expo-router was no longer working anymore, etc

so i removed all metro config and babel configs and I simply installed @better-auth/core package with the version which was listed in the dependency of @better-auth/expo package

yarn workspace my-expo-app add @better-auth/core'^1.4.18'

My Question

  • is this a bad change to fix my issue? right now better auth is working fine for me without any babel or metro config by just installing that exact @better-auth/core package's version for my @better-auth/expo package
<!-- gh-comment-id:3931381203 --> @ashuvssut commented on GitHub (Feb 20, 2026): The Docs BA Bot proposed this solution to me by referencing this GitHub issue BA Bot proposed solution:- > > The error persists because Metro bundler still cannot resolve the internal `@better-auth/core/utils` dependency from the `@better-auth/expo/client` package[](https://github.com/better-auth/better-auth/issues/7218). This is a known Metro bundling issue that affects both iOS and Android platforms[](https://github.com/better-auth/better-auth/issues/7218). > > Since the basic workaround didn't resolve the issue, try this more comprehensive configuration: > > **1. Update your `babel.config.js` with all Better Auth aliases:** > > > ```js > module.exports = function (api) { > api.cache(true); > return { > presets: ["babel-preset-expo"], > plugins: [ > [ > "module-resolver", > { > alias: { > "better-auth/react": "./node_modules/better-auth/dist/client/react/index.mjs", > "better-auth/client/plugins": "./node_modules/better-auth/dist/client/plugins/index.mjs", > "@better-auth/expo/client": "./node_modules/@better-auth/expo/dist/client.mjs", > }, > }, > ], > ], > }; > }; > > ``` > > [](https://github.com/better-auth/better-auth/issues/7491) > > **2. Update your `metro.config.js` with the complete configuration:** > > > ```js > const { getDefaultConfig } = require("expo/metro-config"); > > const config = getDefaultConfig(__dirname); > config.resolver.unstable_enablePackageExports = true; > config.resolver.unstable_conditionNames = [ > "react-native", > "import", > "require", > "browser", > "types", > ]; > > module.exports = config; > > ``` > > [](https://github.com/better-auth/better-auth/issues/7491) > > **3. Clear Metro's cache and rebuild your dev client:** > > > ```bash > npx expo start --clear > > ``` > > [](https://www.better-auth.com/docs/integrations/expo) > > The issue is caused by the ESM-only migration in Better Auth where packages only ship `.mjs` files and Metro's internal resolution logic sometimes bypasses Babel aliases or expects a CJS fallback that doesn't exist[](https://github.com/better-auth/better-auth/issues/7491). This is a deeper Metro bundler issue with ESM resolution that can affect both Android and iOS depending on your project setup[](https://github.com/better-auth/better-auth/issues/7491). > > If you continue experiencing the error after these changes, you may need to wait for an official fix as this is being tracked as a known issue[](https://github.com/better-auth/better-auth/issues/7218). None of the above solutions worked for me. **_Its maybe because I am using a MONOREPO SETUP._** For babel module-transformer, I adjusted the node_modules path to point to the correct `dist/client.mjs` path, but it introduced a lot of other issues like expo-router was no longer working anymore, etc so i removed all metro config and babel configs and I simply installed `@better-auth/core` package with the version which was listed in the dependency of [`@better-auth/expo` package](https://www.npmjs.com/package/@better-auth/expo?activeTab=code) ``` yarn workspace my-expo-app add @better-auth/core'^1.4.18' ``` **My Question** - is this a bad change to fix my issue? right now better auth is working fine for me without any babel or metro config by just installing that exact @better-auth/core package's version for my @better-auth/expo package
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28147