Skip to content

Commit 419fbb2

Browse files
d-csclaude
andcommitted
fix(webapp): dashboard run-detail RunPresenter doesn't 'PrismaClient error' on buffered runs
`RunPresenter.call` used `prisma.taskRun.findFirstOrThrow` to load the run-detail data. For any buffered run (the snapshot lives in Redis, not PG), the throw fires every page-poll tick and Prisma's `writer` emits a `PrismaClient error` log line each time — both the run-detail revalidation poll (~5s) and any Remix `_data` request hit it. The route loader already catches arbitrary errors from `presenter.call` and falls back to the mollifier buffer via `tryMollifiedRunFallback`, so the page renders correctly. But the per-tick error spam pollutes Sentry / log aggregation, makes real DB issues hard to spot, and (in `pnpm dev`) triggers Remix's dev-mode error overlay on every poll. Switch to `findFirst` + explicit null check that throws a typed `RunNotInPgError`. The route catches it the same way — the not-found-in-PG case is now off Prisma's error path entirely. `Run EnvironmentMismatchError` is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0aa815a commit 419fbb2

1 file changed

Lines changed: 25 additions & 1 deletion

File tree

apps/webapp/app/presenters/v3/RunPresenter.server.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ export class RunEnvironmentMismatchError extends Error {
2020
}
2121
}
2222

23+
// Thrown by `call()` when the run isn't in PG. The route loader catches
24+
// this and falls back to the mollifier buffer via `tryMollifiedRunFallback`.
25+
// Using a typed error (rather than Prisma's `findFirstOrThrow` exception)
26+
// keeps the buffered case off the PrismaClient error path — that path
27+
// emits a `PrismaClient error` log every time it fires, which on the
28+
// run-detail page polls becomes per-tick log spam and Sentry noise for
29+
// any run that legitimately lives in the buffer.
30+
export class RunNotInPgError extends Error {
31+
constructor(public readonly runFriendlyId: string) {
32+
super(`Run ${runFriendlyId} not in PG`);
33+
this.name = "RunNotInPgError";
34+
}
35+
}
36+
2337
export class RunPresenter {
2438
#prismaClient: PrismaClient;
2539

@@ -42,7 +56,13 @@ export class RunPresenter {
4256
showDeletedLogs: boolean;
4357
showDebug: boolean;
4458
}) {
45-
const run = await this.#prismaClient.taskRun.findFirstOrThrow({
59+
// `findFirst` + explicit null check (not `findFirstOrThrow`) because
60+
// a missing PG row is the *expected* path for buffered runs — the
61+
// route catches `RunNotInPgError` and falls back to the synthesised
62+
// buffer view. `findFirstOrThrow` would log a `PrismaClient error`
63+
// every tick of the page poll, masking real DB issues with synthetic
64+
// not-found noise.
65+
const run = await this.#prismaClient.taskRun.findFirst({
4666
select: {
4767
id: true,
4868
createdAt: true,
@@ -106,6 +126,10 @@ export class RunPresenter {
106126
},
107127
});
108128

129+
if (!run) {
130+
throw new RunNotInPgError(runFriendlyId);
131+
}
132+
109133
if (environmentSlug !== run.runtimeEnvironment.slug) {
110134
throw new RunEnvironmentMismatchError(
111135
`Run ${runFriendlyId} is not in environment ${environmentSlug}`

0 commit comments

Comments
 (0)