Description
When deploying a TanStack Start application with @convex-dev/better-auth to serverless platforms (Netlify, Vercel), the application crashes at runtime with:
TypeError: getRequestHeaders is not a function
at file:///var/task/.netlify/v1/functions/server.mjs:650:25
at async getToken (file:///var/task/.netlify/v1/functions/server.mjs:671:19)
at async Object.serverFn (file:///var/task/.netlify/v1/functions/server.mjs:732:14)
The build succeeds without errors, but the error occurs when visiting the deployed URL.
Environment
@convex-dev/better-auth: ^0.12.2
@tanstack/react-start: ^1.168.13
better-auth: ^1.6.11
convex: ^1.39.1
- Deployment platforms: Netlify, Vercel
Root Cause
The issue is in @convex-dev/better-auth/dist/react-start/index.js at line 62:
const cachedGetToken = cache(async (opts) => {
const { getRequestHeaders } = await import("@tanstack/react-start/server");
const headers = getRequestHeaders();
// ...
});
The dynamic import await import("@tanstack/react-start/server") fails to resolve correctly in serverless environments, returning a module object where getRequestHeaders is undefined.
This happens because:
- Serverless bundlers (Vite/Nitro) may not correctly resolve dynamic imports at runtime
- The module resolution in serverless environments differs from local development
- Tree-shaking or module externalization can cause the export to be missing
Reproduction
- Create a TanStack Start app with
@convex-dev/better-auth
- Configure auth following the official guide
- Deploy to Netlify or Vercel
- Visit the deployed URL → Error occurs
Minimal auth-server.ts
import { convexBetterAuthReactStart } from "@convex-dev/better-auth/react-start";
export const {
handler,
getToken,
fetchAuthQuery,
fetchAuthMutation,
fetchAuthAction,
} = convexBetterAuthReactStart({
convexUrl: process.env.VITE_CONVEX_URL!,
convexSiteUrl: process.env.VITE_CONVEX_SITE_URL!,
});
Minimal __root.tsx using getToken
import { createServerFn } from "@tanstack/react-start";
import { getToken } from "../lib/auth-server";
const getAuth = createServerFn({ method: "GET" }).handler(async () => {
return await getToken();
});
export const Route = createRootRouteWithContext({
beforeLoad: async (ctx) => {
const token = await getAuth();
// ...
},
});
Workaround / Fix
Option 1: Patch the library (Recommended)
Use patch-package to replace the dynamic import with a static import:
File: patches/@convex-dev+better-auth+0.12.2.patch
diff --git a/node_modules/@convex-dev/better-auth/dist/react-start/index.js b/node_modules/@convex-dev/better-auth/dist/react-start/index.js
index 3a2c9f1..b7d8e2a 100644
--- a/node_modules/@convex-dev/better-auth/dist/react-start/index.js
+++ b/node_modules/@convex-dev/better-auth/dist/react-start/index.js
@@ -1,6 +1,7 @@
import { stripIndent } from "common-tags";
import { ConvexHttpClient } from "convex/browser";
import { getToken } from "../utils/index.js";
+import { getRequestHeaders } from "@tanstack/react-start/server";
import React from "react";
// Caching supported for React 19+ only
const cache = React.cache ||
@@ -59,7 +60,6 @@ const handler = (request, opts) => {
export const convexBetterAuthReactStart = (opts) => {
const siteUrl = parseConvexSiteUrl(opts.convexSiteUrl);
const cachedGetToken = cache(async (opts) => {
- const { getRequestHeaders } = await import("@tanstack/react-start/server");
const headers = getRequestHeaders();
const mutableHeaders = new Headers(headers);
mutableHeaders.delete("content-length");
Add to package.json:
{
"scripts": {
"postinstall": "patch-package"
},
"devDependencies": {
"patch-package": "^8.0.1"
}
}
Option 2: Vite configuration
Also ensure these modules are bundled (not externalized) in vite.config.ts:
export default defineConfig({
// ... other config
ssr: {
noExternal: [
"@convex-dev/better-auth",
"@tanstack/react-start",
"@tanstack/react-start-server",
"@tanstack/start-server-core",
"h3-v2",
"@tanstack/start-client-core",
"@tanstack/react-start-client",
],
},
});
Suggested Fix
Replace the dynamic import with a static import in the library source:
Current code:
const cachedGetToken = cache(async (opts) => {
const { getRequestHeaders } = await import("@tanstack/react-start/server");
const headers = getRequestHeaders();
// ...
});
Suggested code:
import { getRequestHeaders } from "@tanstack/react-start/server";
const cachedGetToken = cache(async (opts) => {
const headers = getRequestHeaders();
// ...
});
Since getRequestHeaders is only used within server functions (which run server-side), a static import is safe and more reliable across different deployment targets.
Additional Context
- The error only occurs in production/serverless builds, not in local development (
npm run dev)
- The issue affects both Netlify and Vercel deployments
- The
@tanstack/react-start/server export chain is: @tanstack/react-start/server → @tanstack/react-start-server → @tanstack/start-server-core → h3-v2
- All these modules correctly export
getRequestHeaders, but the dynamic import resolution fails at runtime in bundled serverless functions
Description
When deploying a TanStack Start application with
@convex-dev/better-authto serverless platforms (Netlify, Vercel), the application crashes at runtime with:The build succeeds without errors, but the error occurs when visiting the deployed URL.
Environment
@convex-dev/better-auth:^0.12.2@tanstack/react-start:^1.168.13better-auth:^1.6.11convex:^1.39.1Root Cause
The issue is in
@convex-dev/better-auth/dist/react-start/index.jsat line 62:The dynamic import
await import("@tanstack/react-start/server")fails to resolve correctly in serverless environments, returning a module object wheregetRequestHeadersisundefined.This happens because:
Reproduction
@convex-dev/better-authMinimal
auth-server.tsMinimal
__root.tsxusinggetTokenWorkaround / Fix
Option 1: Patch the library (Recommended)
Use
patch-packageto replace the dynamic import with a static import:File:
patches/@convex-dev+better-auth+0.12.2.patchAdd to
package.json:{ "scripts": { "postinstall": "patch-package" }, "devDependencies": { "patch-package": "^8.0.1" } }Option 2: Vite configuration
Also ensure these modules are bundled (not externalized) in
vite.config.ts:Suggested Fix
Replace the dynamic import with a static import in the library source:
Current code:
Suggested code:
Since
getRequestHeadersis only used within server functions (which run server-side), a static import is safe and more reliable across different deployment targets.Additional Context
npm run dev)@tanstack/react-start/serverexport chain is:@tanstack/react-start/server→@tanstack/react-start-server→@tanstack/start-server-core→h3-v2getRequestHeaders, but the dynamic import resolution fails at runtime in bundled serverless functions