Skip to content

server-only Module Causes Build Failure in Development Mode (Inconsistent Behavior Between Development and Production Builds) #1967

Closed

Description

@jadedevin13

Describe the bug

The project encounters a build failure in development mode when attempting to import the server-only module from a file meant to handle server-side logic. This issue does not occur in the production build, leading to inconsistent behavior between development and production environments.

Your Example Website or App

N/A

Steps to Reproduce the Bug or Issue

In start-basic-rsc sample.

In app/utils/posts.tsx put the following

// import { notFound } from '@tanstack/react-router'
// import { createServerFn } from '@tanstack/start'
import axios from 'redaxios'
import { getPost } from './static-post'
import { createServerFn } from '@tanstack/start'

export type PostType = {
  id: string
  title: string
  body: string
}

export const fetchPost = async (postId: string) => {
  'use server'
  console.info(`Fetching post with id ${postId}...`)
  await new Promise((r) => setTimeout(r, 500))
  const post = await axios
    .get<PostType>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
    .then((r) => r.data)
    .catch((err) => {
      if (err.status === 404) {
        // throw notFound()
      }
      throw err
    })

  return post
}

export const fetchPosts = async () => {
  'use server'
  console.info('Fetching posts...')
  await new Promise((r) => setTimeout(r, 500))
  return getServerPost()
}

export const getServerPost = createServerFn('GET', () => {
  return getPost()
})

Then add a new file static-post.ts in the same folder with the following contents.

import 'server-only'

export function getPost() {
  return [
    {
      userId: 1,
      id: 1,
      title:
        'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
      body: 'quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto',
    },
  ]
}

If you try to build the project and start it. It will work fine and will not include the server only files, but in dev it will fail

2:43:47 [vite] Error when evaluating SSR module /app/utils/posts.tsx: failed to import "server-only"
|- Error: This module cannot be imported from a Client Component module. It should only be used from a Server Component.

Expected behavior

The project should build and start successfully in both development and production modes without encountering import errors.

Screenshots or Videos

No response

Platform

  • OS: macOS
  • Browser: N/A
  • Version: 1.45.2

Additional context

No response

Activity

  1. uap-dev commented on Mar 8, 2025

    @uap-dev

    More of a question, but would also be curious to know if this is the intended way to exclude certain files from client bundle / whether tanstack start intends to have any pragmas for excluding server side only code.

  2. DonielMoins commented on Apr 18, 2025

    @DonielMoins

    If you're using tanstack start, I found an interesting "solution " to this problem.

    I'm not sure if it will compile everything to the browser version as well, but for me, it saved me from having to ship @prisma/client to my browser bundle. So I recommend you try it out with that get-posts function, then do a quick ctrl + f in your browser bundle.

    here is a short snippit of what I did

    import { PrismaClient } from '@prisma/client'
    import { serverOnly } from '@tanstack/react-start';
    
    
    const createPrismaClient = serverOnly(() =>
        new PrismaClient({
            log:
                process.env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
        }));
    
    const globalForPrisma = globalThis as unknown as {
        prisma: ReturnType<typeof createPrismaClient> | undefined;
    };
    
    export const db = createPrismaClient();
    
    if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db;

    all it does now is throw an error in the console that a server function should only be called from the server.
    Also, the only reason I am proposing this as a "solution" is due to the fact that there is no documented built-in which does something similar. This is very much putting a "band aid" on the problem.
    Please do NOT ship something like this into prod.

    If there is an actual supported solution somewhere out there, it would really be appreciated

  3. Nedi11 commented on Jul 10, 2025

    @Nedi11

    Any update

  4. LiniovasDovydas commented on Jul 12, 2025

    @LiniovasDovydas

    I am using redis, but it seems the problem is the same, I've done the following and it seems it does not produce server only errors or attempts to include redis bundle into the client. The key part is dynamically importing the client (could probably work for prisma client as well)

    import type { createClient as _createClient } from "redis";
    
    import { createMiddleware } from "@tanstack/react-start";
    
    let redis: ReturnType<typeof _createClient> | null = null;
    
    function createRedisServerMiddleware() {
      return createMiddleware({
        type: "function",
        validateClient: false,
      }).server(async ({ next }) => {
        if (!redis) {
          const { createClient } = await import("redis");
    
          redis ??= createClient({
            url: `redis://${process.env.REDIS_ENDPOINT}`,
          });
        }
    
        // eslint-disable-next-line no-console
        redis.on("error", (err) => console.log("Redis Client Error", err));
    
        if (!redis.isReady) {
          await redis.connect().catch((err) => {
            // eslint-disable-next-line no-console
            console.error("Failed to connect to Redis:", err);
          });
        }
    
        const result = await next({ context: { redis } });
    
        redis.close();
        return result;
      });
    }
    
    export { createRedisServerMiddleware };
  5. max-programming commented on Jul 24, 2025

    @max-programming
    Contributor

    I get an error because I use the postgres library. Through the imports (db -> server function -> client side) it tries to execute postgres on the client side and that gives me the error - Buffer is not defined

    This is how I solved my issue: #3401 (comment)

  6. Ynng commented on Aug 19, 2025

    @Ynng

    getting the same Buffer is not defined error with postgres and drizzle.
    ended up adopting DonielMoins's hack #1967 (comment)

  7. LadyBluenotes commented on Jul 11, 2026

    @LadyBluenotes
    Member

    This is now covered by Start’s import-protection system. Modules that access server-only code only inside createServerFn or other server compiler boundaries are supported in build and dev mode.

    If you can reproduce a current-version failure with a minimal example, please open a new issue and include the import chain and Start version :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    startEverything about TanStack Start

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions