Skip to content

server crashes when unhandled exception in server function or middleware #5266

Closed

Description

@kkane

Which project does this relate to?

Start

Describe the bug

Server crashes when there are exceptions in server functions in loader, either middleware or handler itself.

Your Example Website or App

https://github.com/TanStack/router/tree/main/examples/react/start-basic

Steps to Reproduce the Bug or Issue

Add middleware.ts:

import { createMiddleware, json } from "@tanstack/react-start";

export const authMiddleware = createMiddleware({ type: "function" }).server(
  async ({ next, context }) => {
    throw json({ message: "Unauthorized: missing token" }, { status: 401 });
    return next();
  }
);

Replace deferred.tsx with following:

import { Await, createFileRoute } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";
import { Suspense, useState } from "react";
import { authMiddleware } from "~/middleware";

const personServerFn = createServerFn({ method: "GET" })
  .middleware([authMiddleware])
  .inputValidator((d: string) => d)
  .handler(({ data: name }) => {
    return { name, randomNumber: Math.floor(Math.random() * 100) };
  });

const slowServerFn = createServerFn({ method: "GET" })
  .inputValidator((d: string) => d)
  .handler(async ({ data: name }) => {
    await new Promise((r) => setTimeout(r, 1000));
    return { name, randomNumber: Math.floor(Math.random() * 100) };
  });

export const Route = createFileRoute("/deferred")({
  loader: async () => {
    return {
      deferredStuff: new Promise<string>((r) =>
        setTimeout(() => r("Hello deferred!"), 2000)
      ),
      deferredPerson: slowServerFn({ data: "Tanner Linsley" }),
      person: await personServerFn({ data: "John Doe" }),
    };
  },
  component: Deferred,
});

function Deferred() {
  const [count, setCount] = useState(0);
  const { deferredStuff, deferredPerson, person } = Route.useLoaderData();

  return (
    <div className="p-2">
      <div data-testid="regular-person">
        {person.name} - {person.randomNumber}
      </div>
      <Suspense fallback={<div>Loading person...</div>}>
        <Await
          promise={deferredPerson}
          children={(data) => (
            <div data-testid="deferred-person">
              {data.name} - {data.randomNumber}
            </div>
          )}
        />
      </Suspense>
      <Suspense fallback={<div>Loading stuff...</div>}>
        <Await
          promise={deferredStuff}
          children={(data) => <h3 data-testid="deferred-stuff">{data}</h3>}
        />
      </Suspense>
      <div>Count: {count}</div>
      <div>
        <button onClick={() => setCount(count + 1)}>Increment</button>
      </div>
    </div>
  );
}

then go directly to http://localhost:3000/deferred (page needs to be initially loaded from server at that path).
Server crashes with:

Error reading routerStream: g [Error]: The value [object Response] of type "object" cannot be parsed/serialized.

Another way to make it crash:

const personServerFn = createServerFn({ method: "GET" })
  .inputValidator((d: string) => d)
  .handler(({ data: name }) => {
    throw new Error("test");
    return { name, randomNumber: Math.floor(Math.random() * 100) };
  });

and then remove await in loader:

person: personServerFn({ data: "John Doe" }),

Server crashes with exception:

    throw new Error("test");
          ^
Error: test

Expected behavior

Expect server to never crash due to user's code.

Platform

  • Router / Start Version:
    "@tanstack/react-start": "^1.132.13",
    "@tanstack/react-router": "^1.132.7",

Activity

  1. uncvrd commented on Nov 10, 2025

    @uncvrd

    Not sure how different but thought I'd mention. This also occurs when using useServerFn which is supposed to catch and handle redirects, seeing the same error.

        const test = useServerFn(testServerFn)
    
        const { mutateAsync: createBilling, isPending } = useMutation({
            mutationFn: test,
            async onSuccess() {
                return await onInvalidateQueries()
            },
        })
    

    Where

    export const testServerFn = createServerFn({ method: "POST" })
        .middleware([withPermission("billing.write")]) <!-- if I remove this it works (even though I don't throw an error in here)
        .inputValidator(z.object({ workspaceId: z.number() }))
        .handler(async ({ context, data: { workspaceId } }) => {
            throw redirect({
                to: "/app/workspace",
            })
        })
    

    Docs reference using the hook here: https://tanstack.com/start/latest/docs/framework/react/guide/server-functions#error-handling--redirects

    EDIT: cross ref: #5372 (comment) and #4460

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions