Vercel Bun runtime crashes with “Requested module is not instantiated yet” when using React TSX SSR

Hello Vercel Community,

Could you please help for the issue i am facing? when i deploy bun express project to Vercel.

Steps i did before creating this topic:


Current behavior

The deployment builds successfully, but the Serverless Function crashes at runtime when a React TSX SSR route is introduced.

Visiting the deployed URL results in:

This Serverless Function has crashed.

500: INTERNAL_SERVER_ERROR
Code: FUNCTION_INVOCATION_FAILED
ID: bom1:bom1::795sp-1766311001732-983203ba7892

Deployment URL:

ecom-api-12a876as1-dy1-projects.vercel.app

The same code works perfectly locally using Bun.

If I remove the TSX / React SSR code and revert to static .htm files, the deployment works again.


Expected behavior

  • React SSR using .tsx should work in a Bun + Express serverless function
  • Or at least fail at build-time with a clear error
  • The function should not crash at runtime after a successful build

Runtime error logs (Serverless Function)

2025-12-21 09:56:43.756 [error] TypeError: Requested module is not instantiated yet.
at link (native:1:11)
at link (native:1:11)
at link (native:1:11)
at linkAndEvaluateModule (native:1:11)
at requestImportModule (native:2)
at processTicksAndRejections (native:7:39)

Bun process exited with exit status: 1.
The logs above can help with debugging the issue.

This error happens only when TSX + React SSR is used.


Build logs (successful)

Running "vercel build"
Vercel CLI 50.1.3

Installing dependencies...
bun install v1.3.4

* react@19.2.3
* react-dom@19.2.3
* @types/react@19.2.7
* @types/react-dom@19.2.3

Running "bun run build"
$ tsc --noEmit
Using TypeScript 5.8.3

Build Completed
Deployment completed successfully

So:

  • Build passes
  • TypeScript passes
  • Crash happens only at runtime

Code file index.ts

import { OpenAPIHandler } from "@orpc/openapi/node";
import { onError } from "@orpc/server";
import { apiReference } from "@scalar/express-api-reference";
import cookieParser from "cookie-parser";
import cors from "cors";
import express, { NextFunction, Request, Response } from "express";
import { connectDB } from "./lib/mongo.js";
import { apiRateLimit, openApiAccess, serverGate } from "./middleware/index.js";
import { generateOpenAPISpec } from "./orpc/openapi.js";
import { orpcRouter } from "./orpc/router.js";
import { aboutRouter, healthzRouter, homeRouter, loginRouter } from "./routes/index.js";

const app = express();

// ===============================================
// Core middleware
// ===============================================

app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// ===============================================
// Database connection
// ===============================================

app.use(async (_req, res, next) => {
  try {
    await connectDB();
    next();
  } catch (err: unknown) {
    console.error("DB init failed:", err);
    res.status(503).json({
      success: false,
      status: 503,
      message: "Database unavailable",
    });
  }
});

// ===============================================
// CORS
// ===============================================

app.use(
  cors({
    origin: [
      "https://r9k0j6rn-3000.inc1.devtunnels.ms",
      "http://localhost:3000",
    ],
    credentials: true,
    allowedHeaders: [
      "Content-Type",
      "Authorization",
      "X-Client-Id",
      "X-Client-Secret",
    ],
    methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
    optionsSuccessStatus: 204,
  })
);

// ===============================================
// Static assets
// ===============================================

app.use(express.static("public"));

// ===============================================
// Rate limit
// ===============================================

app.set("trust proxy", 1);

app.use("/api", apiRateLimit);

// ===============================================
// Docs login (ONLY public thing)
// ===============================================

app.use("/login", loginRouter);

// ===============================================
// GLOBAL SERVER GATE (ABSOLUTE)
// ===============================================

app.use((req, res, next) => {
  // 1. Internal Next.js frontend → always allowed
  if (serverGate(req)) {
    return next();
  }

  // 2. Allow login page itself (avoid infinite redirect)
  if (req.path.startsWith("/login")) {
    return next();
  }

  // 3. Logged-in browser (cookie)
  if (req.cookies?.auth === "ok") {
    return next();
  }

  // 4. EVERYTHING ELSE → redirect to login
  return res.redirect("/login");
});

// ===============================================
// OpenAPI JSON (PROTECTED)
// ===============================================

app.get("/api/openapi.json", openApiAccess, async (_req, res) => {
  const spec = await generateOpenAPISpec();
  res.json(spec);
});

// ===============================================
// API Docs (Scalar UI) — PROTECTED
// ===============================================

app.use(
  "/api/docs",
  apiReference({
    url: "/api/openapi.json",
  })
);

// ===============================================
// HTML routes (PROTECTED)
// ===============================================

app.use("/", homeRouter);
app.use("/about", aboutRouter);

// ===============================================
// Health check (PROTECTED)
// ===============================================

app.use("/healthz", healthzRouter);

// ===============================================
// oRPC REST handler (PROTECTED)
// ===============================================

const orpcHandler = new OpenAPIHandler(orpcRouter, {
  interceptors: [
    onError((_error: unknown) => {
      // optional logging
      // console.error("[oRPC error]", error);
    }),
  ],
});

app.use(async (req, res, next) => {
  const result = await orpcHandler.handle(req, res, {
    // prefix: "/api",

    context: {
      headers: req.headers,
    },
  });

  if (result.matched) {
    return;
  }

  next();
});

// ===============================================
// Global error handler
// ===============================================

app.use((err: unknown, req: Request, res: Response, _next: NextFunction) => {
  const msg = err instanceof Error ? err.stack : 'Something went wrong!';

  console.error(`Global Error - ${msg}`);
  res.status(500).json({
    success: false,
    statusCode: 500,
    error: "Something went wrong",
  });
});

export default app;

Code that triggers the issue (login route only)

src/routes/login.tsx

import type { Request, Response } from "express";
import { Router } from "express";
import { Lock, User } from "lucide-react";
import React from "react";
import { renderToString } from "react-dom/server";

const router = Router();

type Props = {
  title: string;
};

function Login({ title }: Props): React.ReactElement {
  return (
    <html lang="en">
      <head>
        <meta charSet="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>{title}</title>
        <link rel="stylesheet" href="/style.css" />
        <link rel="stylesheet" href="/login.css" />
      </head>
      <body>
        <div className="login-body">
          <div className="login-container">
            <h2>Ecom Server API Login</h2>

            <form method="POST">
              <div>
                <User />
                <input name="username" placeholder="Username" required />
              </div>
              <div>
                <Lock />
                <input
                  name="password"
                  type="password"
                  placeholder="Password"
                  required
                />
              </div>
              <button type="submit">Sign In</button>
            </form>
          </div>
        </div>
      </body>
    </html>
  );
}

router.get("/", async (_req: Request, res: Response) => {
  const html = renderToString(<Login title="Login | Ecom API Hub" />);
  return res.type("html").send("<!DOCTYPE html>" + html);
});

export default router;

Configuration

vercel.json

{
  "bunVersion": "1.x"
}

package.json (relevant parts)

{
  "type": "module",
  "scripts": {
    "build": "tsc --noEmit"
  },
  "dependencies": {
    "express": "^5.2.1",
    "react": "^19.2.3",
    "react-dom": "^19.2.3",
    "lucide-react": "^0.562.0"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "jsx": "react-jsx",
    "module": "NodeNext",
    "types": ["bun"],
    "skipLibCheck": true
  }
}

Notes / Observations

  • The app is a pure Express server, not Next.js
  • app is exported correctly from src/index.ts
  • The serverless function is detected correctly (runtime: bun1.x)
  • The crash happens only when importing React / TSX - i believe.
  • Local Bun runtime works fine with the same code

Questions

  1. Is React SSR with TSX officially supported in Bun-based Vercel Serverless Functions?

  2. Is a manual prebuild step (TSX → JS) required for this setup? I dont know how, need help.

  3. What causes the runtime error:

    TypeError: Requested module is not instantiated yet
    

    in this context?

  4. Is this a known limitation of:

    • Bun runtime on Vercel?
    • React 19 with SSR?
    • ESM + NodeNext in serverless functions?

Any guidance or confirmation would be very helpful.

Thanks!

Regards

Dheeraj

If you’re having trouble deploying an Express app, this guide can help.

You can also ask v0 for suggestions tailored to your own project setup.

Could anyone provide analysis why this react tsx usage is causing Vercel deployment issues? We should not use react tsx code in routes in backend bun runtime, but why it works locally?

ok i am closing as this post received no help from community and i am yet to find the solution thought i can’t as I am unable to understand the issue.

Hey there, @yssdhiraj-4670! Just checking in on your issue with the Vercel deployment and the React TSX usage. Have you found a solution, or do you still need some help? Excited to help!