8000 TRPC Middleware Context not Working as Expected with Batch Requests · Issue #16262 · getsentry/sentry-javascript · GitHub
[go: up one dir, main page]

Skip to content

TRPC Middleware Context not Working as Expected with Batch Requests #16262

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
3 tasks done
17Amir17 opened this issue May 12, 2025 · 5 comments · Fixed by #16296
Closed
3 tasks done

TRPC Middleware Context not Working as Expected with Batch Requests #16262

17Amir17 opened this issue May 12, 2025 · 5 comments · Fixed by #16296

Comments

@17Amir17
Copy link

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/nextjs

SDK Version

9.17.0

Framework Version

React Next 15.3.1

Link to Sentry event

https://amir-lf.sentry.io/issues/41286609/?project=4509151724830800&query=is%3Aunresolved%20issue.priority%3A%5Bhigh%2C%20medium%5D&referrer=issue-stream&stream_index=0

Reproduction Example/SDK Setup

Create a repo with minimal code that reproduces this https://github.com/17Amir17/SentryBug
Repo contains a simple nextjs app with trpc and sentry

One route called getCats this route accepts two params one which when true throws an exception and the the other contains an id which gets added as a tag - https://github.com/17Amir17/SentryBug/blob/main/src/pages/api/trpc/%5Btrpc%5D.ts

Steps to Reproduce

There are two problem that I see, to reproduce the first

  1. Click "throw error" button
  2. Two calls to the same trpc route will be called in batch with different parameters
  3. A tag is added with the cat id
  4. And error is thrown
  5. 1 sentry event is created
  6. Tag does not match the context

To reproduce the second

  1. Click fetch button
  2. Two calls to the same trpc route will be called in batch with different parameters
  3. A tag is added with the cat id
  4. 2 sentry events are created
  5. Tag does not match context

Expected Result

For both issues I'd expect to see two events and for the context to match the tag

Actual Result

For the first issue the tags do not match the context and there is only 1 event created

For the second issue the there are two events but the tag does not match the context

Image

@17Amir17 17Amir17 added the Bug label May 12, 2025
@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 May 12, 2025
@andreiborza
Copy link
Member

Hi @17Amir17, thanks for filing this.

This happens because there's no automatic scope forking going on for the middleware. We're looking into possible workarounds/solutions.

@andreiborza
Copy link
Member

Because there's no automatic scope forking, you'd have to fork your own scope using for example the Sentry.withScope api.

Your trpc api would have to look something like this:

...
    .query(async ({ input }) => {
      return Sentry.withScope(async (scope) => {
        scope.setTag("catRequestId", input.catRequestId);

        await new Promise((resolve) => setTimeout(resolve, 1000));
        if (input.shouldThrow) {
            throw new Error("Error triggered by shouldThrow in getCats");
        }
        Sentry.captureMessage("getCats success");
        return ["Whiskers", "Mittens", "Shadow"];
      });
    }),
});

Now, this works well for the second case, but it does not work for the first case because scope data is picked up when the error is captured, which happens in our trpc middleware. By then, the scope we fork ourselves is already gone and so is its data.

If you want to solve the first case, you'd have to manually try/catch throwing code and capture the error manually using Sentry.captureException:

...
    .query(async ({ input }) => {
      return Sentry.withScope(async (scope) => {
        scope.setTag("catRequestId", input.catRequestId);

        await new Promise((resolve) => setTimeout(resolve, 1000));
        if (input.shouldThrow) {
          try {
            throw new Error("Error triggered by shouldThrow in getCats");
          } catch (error) {
            Sentry.captureException(error);
          }

          return;
        }
        Sentry.captureMessage("getCats success");
        return ["Whiskers", "Mittens", "Shadow"];
      });
    }),

Hope this helps.

andreiborza added a commit that referenced this issue May 14, 2025
Writing tRPC handlers today and using top-level methods like `Sentry.setTag`
isn't very intuitive as the isolations scope is not forked per procedure in our
middleware.

This PR changes the middleware to fork the isolation scope, while this is not
100% correct, as it breaks the one isolation scope per process/request model,
it should be more intuitive and work better for most users.

Resolves: #16262
Copy link
Contributor

A PR closing this issue has just been released 🚀

This issue was referenced by PR #16296, which was included in the 9.20.0 release.

@andreiborza
Copy link
Member

@17Amir17 this should now work out of the box without having to fork the scope yourself.

@17Amir17
Copy link
Author

BEAST thanks <3

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

Successfully merging a pull request may close this issue.

2 participants
0