Skip to content

Commit c265cc7

Browse files
d-csclaude
andcommitted
fix(webapp): narrow run-detail buffer fallback to RunNotInPgError
The loader's catch block was treating every RunPresenter error as a PG miss and trying the mollifier buffer fallback. That meant a downstream trace-query or event-repository failure after the PG row was found would either return stale synth data (if a buffer entry happened to still be present during the drainer materialisation race window) or silently mask the real error. Narrow the fallback to `RunNotInPgError` — the typed signal RunPresenter throws specifically for "this run is not in PG and may be in the buffer" — and re-throw all other errors. Matches the design intent stated on the class itself. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6b1d36a commit c265cc7

1 file changed

Lines changed: 17 additions & 1 deletion

File tree

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ import { env } from "~/env.server";
9292
import { findProjectBySlug } from "~/models/project.server";
9393
import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
9494
import { NextRunListPresenter } from "~/presenters/v3/NextRunListPresenter.server";
95-
import { RunEnvironmentMismatchError, RunPresenter } from "~/presenters/v3/RunPresenter.server";
95+
import {
96+
RunEnvironmentMismatchError,
97+
RunNotInPgError,
98+
RunPresenter,
99+
} from "~/presenters/v3/RunPresenter.server";
96100
import { findRunByIdWithMollifierFallback } from "~/v3/mollifier/readFallback.server";
97101
import { buildSyntheticRunHeader } from "~/v3/mollifier/syntheticRunHeader.server";
98102
import { buildSyntheticTraceForBufferedRun } from "~/v3/mollifier/syntheticTrace.server";
@@ -281,6 +285,18 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
281285
);
282286
}
283287

288+
// Only fall back to the mollifier buffer on a genuine PG miss. Any
289+
// other error (DB timeout during trace queries, event-repository
290+
// failure, etc.) means the run WAS in PG but a downstream lookup
291+
// failed — falling back to the buffer here would either return a
292+
// stale synth entry if one happens to exist in the brief drainer-
293+
// materialisation race window, or quietly mask the real failure.
294+
// `RunNotInPgError` is the typed signal RunPresenter throws for the
295+
// route loader's specific case (`RunPresenter.server.ts:130`).
296+
if (!(error instanceof RunNotInPgError)) {
297+
throw error;
298+
}
299+
284300
// PG miss → try the mollifier buffer. When the gate diverts a trigger
285301
// the run sits in Redis until the drainer materialises it; without
286302
// this fallback the run-detail page 404s for the brief buffered window

0 commit comments

Comments
 (0)