-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Is there an existing issue for this?
- I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- I have reviewed the documentation https://docs.sentry.io/
- I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/nextjs
SDK Version
10.32.1
Framework Version
Next.js 16.1.13
Link to Sentry event
No response
Reproduction Example/SDK Setup
Minimal reproduction:
# 1. Create fresh Next.js 16 project with default settings
pnpm dlx create-next-app@latest sentry-test
cd sentry-test
# 2. Run Sentry wizard with defaults
pnpm dlx @sentry/wizard@latest -i nextjs
# 3. Start dev server (uses Turbopack by default)
pnpm dev
# 4. Visit http://localhost:3000/sentry-example-page and click "Throw Sample Error"
# 5. Check Sentry dashboard - backend error never appearsThe wizard generates these files with default settings:
package.json:
{
"dependencies": {
"@sentry/nextjs": "^10.34.0",
"next": "16.1.3",
"react": "19.2.3"
}
}sentry.server.config.ts (wizard-generated, unmodified):
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "https://[email protected]/xxx",
tracesSampleRate: 1,
enableLogs: true,
sendDefaultPii: true,
});instrumentation.ts (wizard-generated, unmodified):
import * as Sentry from "@sentry/nextjs";
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
await import("./sentry.server.config");
}
if (process.env.NEXT_RUNTIME === "edge") {
await import("./sentry.edge.config");
}
}
export const onRequestError = Sentry.captureRequestError;app/api/sentry-example-api/route.ts (wizard-generated test route):
import * as Sentry from "@sentry/nextjs";
export const dynamic = "force-dynamic";
class SentryExampleAPIError extends Error {
constructor(message: string | undefined) {
super(message);
this.name = "SentryExampleAPIError";
}
}
export function GET() {
Sentry.logger.info("Sentry example API called");
throw new SentryExampleAPIError(
"This error is raised on the backend called by the example page.",
);
}Steps to Reproduce
- Create a fresh Next.js 16 project:
pnpm dlx create-next-app@latest - Run Sentry wizard:
pnpm dlx @sentry/wizard@latest -i nextjs(accept all defaults) - Start dev server:
pnpm dev(uses Turbopack by default in Next.js 16) - Visit
http://localhost:3000/sentry-example-page - Click "Throw Sample Error" button
- Observe: Frontend error appears in Sentry
- Backend error (
SentryExampleAPIError) never appears
Expected Result
Both the frontend error (SentryExampleFrontendError) and backend error (SentryExampleAPIError) should appear in the Sentry dashboard.
Actual Result
- Frontend error appears in Sentry
- Backend/server-side error never appears
- No errors in console
onRequestErrorhook is called (verified with logging)- Transport returns HTTP 200 from Sentry ingest API
- Events are silently lost
Additional Context
Proof it's the SDK transport:
Using a custom fetch-based transport with identical envelope data works:
// sentry.server.config.ts
import * as Sentry from "@sentry/nextjs";
function makeFetchTransport(options: Parameters<typeof Sentry.makeNodeTransport>[0]) {
return Sentry.createTransport(options, async (request) => {
const response = await fetch(options.url, {
method: "POST",
body: request.body as BodyInit,
headers: options.headers,
});
return {
statusCode: response.status,
headers: {
"x-sentry-rate-limits": response.headers.get("X-Sentry-Rate-Limits"),
"retry-after": response.headers.get("Retry-After"),
},
};
});
}
Sentry.init({
dsn: "...",
transport: makeFetchTransport, // <-- Backend errors now appear in Sentry
});Additional findings:
| Test | Result |
|---|---|
Fresh create-next-app + Sentry wizard (default settings) |
❌ Backend errors lost |
Same setup with next dev --no-turbopack (Webpack) |
✅ Works |
Same http.request + stream.pipe code in standalone Node.js |
✅ Works |
| Custom fetch transport (workaround above) | ✅ Works |
| Client-side errors | ✅ Works |
Additional Context
Suspected root cause
The suppressTracing() wrapper in @sentry/node-core/transports/http.js manipulates OpenTelemetry async context. This may interact badly with Turbopack's async context handling:
// From @sentry/node-core/build/esm/transports/http.js
return new Promise((resolve, reject) => {
suppressTracing(() => { // <-- Async context manipulation
let body = streamFromBody(request.body);
// ...
body.pipe(req); // <-- Stream callbacks may not fire correctly in Turbopack
});
});This is similar to #18866 where suppressTracing() causes tracing spans to be suppressed in serverless environments.
I'm unfamiliar with the intricritacties of OpenTelemetry so using the suggested fix by Opus 4.5 of directly calling fetch() instead feels wrong to me but I'm providing it just in case.
Suggested fix
Since Node.js 18+ has native fetch(), use it by default:
if (typeof globalThis.fetch === 'function') {
return createTransport(options, async (request) => {
const response = await fetch(options.url, {
method: 'POST',
body: request.body,
headers: options.headers,
});
return { statusCode: response.status, headers: {...} };
});
}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
Projects
Status