Skip to content

Commit 6673298

Browse files
d-csclaude
andcommitted
fix(webapp): wrap sentry tenant processor registration in singleton()
Remix's dev mode re-evaluates the entry module on code changes, but `@sentry/remix` lives in node_modules and isn't reloaded — so without a singleton guard each HMR reload was appending another copy of the processor to Sentry's global processor list. Idempotent at runtime (the processor is a pure read+stamp), but the list grew unbounded over a dev session. Matches the pattern used by the two adjacent `singleton()` calls. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b16902a commit 6673298

1 file changed

Lines changed: 10 additions & 3 deletions

File tree

apps/webapp/app/entry.server.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,16 @@ process.on("uncaughtException", (error, origin) => {
291291
singleton("RunEngineEventBusHandlers", registerRunEngineEventBusHandlers);
292292
singleton("SetupBatchQueueCallbacks", setupBatchQueueCallbacks);
293293

294-
if (env.SENTRY_DSN) {
295-
Sentry.addEventProcessor(addTenantContextToEvent);
296-
}
294+
// Wrapped in singleton() so Remix's dev-mode CJS reloads don't append
295+
// duplicate copies of the processor — Sentry's processor list lives in
296+
// node_modules and persists across module reloads. Idempotent at runtime
297+
// (the processor is a pure read+stamp), but the pattern matches the rest
298+
// of this file.
299+
singleton("SentryTenantContextProcessor", () => {
300+
if (env.SENTRY_DSN) {
301+
Sentry.addEventProcessor(addTenantContextToEvent);
302+
}
303+
});
297304

298305
export { apiRateLimiter } from "./services/apiRateLimit.server";
299306
export { engineRateLimiter } from "./services/engineRateLimit.server";

0 commit comments

Comments
 (0)