Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions docs/platforms/javascript/guides/nextjs/tracing/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -357,19 +357,20 @@ Sentry.init({
<SplitSection>
<SplitSectionText>

### Detached spans with `after()`
### Detached spans with `after()` or `waitUntil()`

If you're using Next.js's [`after()`](https://nextjs.org/docs/app/api-reference/functions/after) and you see multiple root spans (like individual `db` spans) instead of one cohesive trace, your spans are becoming detached.
If you're using Next.js's [`after()`](https://nextjs.org/docs/app/api-reference/functions/after) or [`waitUntil`](https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package#waituntil) and you see multiple root spans (like individual `db` spans) instead of one cohesive trace, your spans are becoming detached.

This happens when `after()` runs after the response is sent. By then, the request's root span has already ended.
This happens when `after()` or `waitUntil()` runs after the response is sent. By then, the request's root span has already ended.

Wrap your deferred work with `startSpan` inside the `after()` callback to group all background spans into a single transaction:
Wrap your deferred work with `startSpan` inside the `after()` or `waitUntil()` callback to group all background spans into a single transaction:

</SplitSectionText>
<SplitSectionCode>

```tsx {filename:app/api/webhook/route.ts}
import { after } from "next/server";
import { waitUntil } from '@vercel/functions';
import * as Sentry from "@sentry/nextjs";

export async function POST(request: Request) {
Expand All @@ -382,6 +383,13 @@ export async function POST(request: Request) {
)
);

waitUntil(
Sentry.startSpan(
{ name: "webhook.process", op: "task" },
() => processWebhook(data)
)
);

return Response.json({ received: true });
}
Comment on lines 384 to 394
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The code example incorrectly shows after() and waitUntil() being used together, which will cause the wrapped processWebhook(data) function to execute twice for each request.
Severity: CRITICAL

Suggested Fix

The code example should be updated to show after() and waitUntil() as mutually exclusive alternatives. This can be achieved by providing separate code blocks or using tabs to let the user switch between the after() example and the waitUntil() example. Do not show both being called in the same function body.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: docs/platforms/javascript/guides/nextjs/tracing/index.mdx#L383-L394

Potential issue: The documentation for handling detached spans provides a code example
that simultaneously calls both `after()` and `waitUntil()` with the same
`processWebhook(data)` function. Since both `after()` and `waitUntil()` are designed to
execute a task after the response is sent, a developer copying this example would
inadvertently cause their webhook handler to run twice. This could lead to critical
production issues like duplicate payment charges, duplicate order creation, or redundant
notifications. The surrounding documentation text correctly presents these functions as
alternatives, but the code example is misleadingly structured.

Did we get this right? 👍 / 👎 to inform future reviews.

```
Expand Down
Loading