Skip to content

@sentry/tanstackstart-react: sentryGlobal*Middleware (TanStackMiddlewareBase with ~types: any) breaks TanStack Start middleware context inference #21433

@terijaki

Description

@terijaki

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/tanstackstart-react

SDK Version

10.56.0

Framework Version

@tanstack/react-start: 1.168.2

Link to Sentry event

No response

Reproduction Example/SDK Setup

Minimal src/start.ts + server function (no separate repo):

// src/start.ts
import {
  sentryGlobalFunctionMiddleware,
  sentryGlobalRequestMiddleware,
} from '@sentry/tanstackstart-react';
import { createMiddleware, createStart } from '@tanstack/react-start';

type AppContext = {
  db: { query: (sql: string) => Promise<unknown[]> };
};

const appContextMiddleware = createMiddleware({ type: 'function' }).server(async ({ next }) => {
  return next({
    context: {
      db: { query: async () => [] },
    } satisfies AppContext,
  });
});

export const startInstance = createStart(() => ({
  functionMiddleware: [
    sentryGlobalFunctionMiddleware, // docs: must be first
    appContextMiddleware,
  ],
  requestMiddleware: [sentryGlobalRequestMiddleware],
}));
// server function
import { createServerFn } from '@tanstack/react-start';

export const listItems = createServerFn({ method: 'GET' }).handler(async ({ context }) => {
  const { db } = context;
  return db.query('SELECT 1');
});

Steps to Reproduce

  1. Create a TanStack Start app with global middleware from createMiddleware() (e.g. shared AppContext), per the middleware guide.
  2. Add sentryGlobalFunctionMiddleware and sentryGlobalRequestMiddleware as the first entries in createStart(), per the manual setup docs.
  3. Add a createServerFn handler that reads typed fields from context (see repro above).
  4. Run tsc and/or ESLint with @typescript-eslint/no-unsafe-* enabled (e.g. eslint --max-warnings 0).

Expected Result

context in server function handlers is inferred from the middleware chain (e.g. AppContext plus auth from other middleware). No type or lint errors when using the documented Sentry setup together with typed TanStack middleware.

Actual Result

context (and values derived from it) become any. Many cascading ESLint errors, e.g.:

  • @typescript-eslint/no-unsafe-assignment
  • @typescript-eslint/no-unsafe-member-access
  • @typescript-eslint/no-unsafe-return

Removing the Sentry global middleware restores correct inference; adding it back (docs-exact) breaks it again. Runtime Sentry behavior appears fine — this is a TypeScript typing issue.

Additional Context

Likely cause: TanStackMiddlewareBase declares '~types': any (see packages/tanstackstart-react/src/common/types.ts). TanStack Start merges middleware context via AssignAllMiddleware on ~types.allServerContext. When the first middleware contributes any, merged context in createServerFn handlers becomes any. Runtime objects use '~types': undefined in globalMiddleware.ts, but the declared type is still any. Introduced in #19330.

Workaround:

functionMiddleware: [
  sentryGlobalFunctionMiddleware as unknown as typeof appContextMiddleware,
  appContextMiddleware,
],

Suggested fix: Type the global Sentry middleware so it does not contribute context to the chain — e.g. build it with createMiddleware() internally, or type allServerContext as undefined instead of any.

Sentry's e2e app already combines sentryGlobal* with createMiddleware() middleware: https://github.com/getsentry/sentry-javascript/blob/develop/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/start.ts — the monorepo may not enforce the same strict @typescript-eslint/no-unsafe-* rules as consumer apps.

Similar typing issues elsewhere: #17749 (React Router), #9536 (tRPC).

No existing issue found for TanStackMiddlewareBase / sentryGlobalFunctionMiddleware + context inference.

Priority

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it.

Metadata

Metadata

Assignees

No fields configured for issues without a type.

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions