Files
Tom Moor cf488508b7 fix: Ignore "Premature close" stream errors in Sentry (#12424)
Client disconnects mid-response surface as "Premature close" errors from
Node's stream end-of-stream helper. These are expected and add noise to
Sentry, similar to the EPIPE/ECONNRESET errors already filtered.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 21:26:27 -04:00

94 lines
2.3 KiB
TypeScript

import * as Sentry from "@sentry/node";
import env from "@server/env";
import type { AppContext } from "@server/types";
if (env.SENTRY_DSN) {
Sentry.init({
dsn: env.SENTRY_DSN,
environment: env.ENVIRONMENT,
release: env.RELEASE,
maxBreadcrumbs: 0,
ignoreErrors: [
// These errors are expected in normal running of the application and
// don't need to be reported.
// Validation
"BadRequestError",
"SequelizeValidationError",
"SequelizeEmptyResultError",
"ValidationError",
"ForbiddenError",
// Authentication
"UnauthorizedError",
"TeamDomainRequiredError",
"GmailAccountCreationError",
"AuthRedirectError",
"UserSuspendedError",
"TooManyRequestsError",
// Client disconnects
"Premature close",
],
beforeSend(event) {
try {
switch (event.level) {
case "warning":
// Sample warnings to reduce noise
if (Math.random() < 0.1) {
return null;
}
break;
}
return event;
} catch (_) {
return event;
}
},
});
}
// oxlint-disable-next-line @typescript-eslint/no-explicit-any
export function requestErrorHandler(error: any, ctx: AppContext) {
// we don't need to report every time a request stops to the bug tracker
if (error.code === "EPIPE" || error.code === "ECONNRESET") {
return;
}
if (env.SENTRY_DSN) {
Sentry.withScope(function (scope) {
const requestId = ctx.headers["x-request-id"];
if (requestId) {
scope.setTag("request_id", requestId as string);
}
const authType = ctx.state?.auth?.type ?? undefined;
if (authType) {
scope.setTag("auth_type", authType);
}
const teamId = ctx.state?.auth?.user?.teamId ?? undefined;
if (teamId) {
scope.setTag("team_id", teamId);
}
const userId = ctx.state?.auth?.user?.id ?? undefined;
if (userId) {
scope.setUser({
id: userId,
});
}
scope.addEventProcessor(function (event) {
return Sentry.Handlers.parseRequest(event, ctx.request);
});
Sentry.captureException(error);
});
} else if (env.ENVIRONMENT !== "test") {
// oxlint-disable-next-line no-console
console.error(error);
}
}
export default Sentry;