Skip to content

Commit 372fe78

Browse files
d-csclaude
andcommitted
fix(webapp): logs download for buffered runs returns a placeholder line, not 404
The dashboard's "Download logs" button on a run-detail page generates `/resources/runs/{runFriendlyId}/logs/download`. For a buffered run (no PG row yet) the loader's `prisma.taskRun.findFirst` returned null and the route 404'd. The customer just triggered the run and got back its id, so a 404 here reads as "your run vanished" — when the truth is just "the run hasn't executed, so there are no logs yet." When the PG lookup misses, verify the entry exists in the mollifier buffer and the requesting user belongs to its org. Then stream a gzipped log file containing a single informational line in the same `<timestamp> <task> <level> <message>` shape `formatRunEvent` emits elsewhere in this route: 2026-05-29T07:58:56.839Z hello-world INFO Run is queued, has not started executing yet — no logs to download. A meaningful one-liner beats a 0-byte file when a customer is grepping the download for a clue. The line uses INFO (not TRACE) so it doesn't look like an execution span. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e08dfcd commit 372fe78

1 file changed

Lines changed: 55 additions & 0 deletions

File tree

apps/webapp/app/routes/resources.runs.$runParam.logs.download.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { formatDurationMilliseconds } from "@trigger.dev/core/v3/utils/durations
99
import { getTaskEventStoreTableForRun } from "~/v3/taskEventStore.server";
1010
import { TaskEventKind } from "@trigger.dev/database";
1111
import { getEventRepositoryForStore } from "~/v3/eventRepository/index.server";
12+
import { getMollifierBuffer } from "~/v3/mollifier/mollifierBuffer.server";
1213

1314
export async function loader({ params, request }: LoaderFunctionArgs) {
1415
const user = await requireUser(request);
@@ -30,6 +31,60 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
3031
});
3132

3233
if (!run || !run.organizationId) {
34+
// Buffered run? It hasn't executed, so there are no events to
35+
// stream — but a 404 is wrong: the run does exist, the customer's
36+
// "Download logs" button on the run-detail page generates this
37+
// exact URL, and a 404 reads as "your run vanished" rather than
38+
// "no logs yet". Verify the entry exists in the buffer (with the
39+
// user as a member of the entry's org), and if so stream a single
40+
// informational line in the same `<timestamp> <task> <level>
41+
// <message>` shape `formatRunEvent` uses below — so a downstream
42+
// log viewer / grep over the downloaded file produces a
43+
// meaningful explanation, not a 0-byte mystery.
44+
const buffer = getMollifierBuffer();
45+
if (buffer) {
46+
const entry = await buffer.getEntry(parsedParams.runParam);
47+
if (entry) {
48+
const member = await prisma.orgMember.findFirst({
49+
where: { userId: user.id, organizationId: entry.orgId },
50+
select: { id: true },
51+
});
52+
if (member) {
53+
let taskIdentifier: string | undefined;
54+
try {
55+
const snapshot = JSON.parse(entry.payload) as { taskIdentifier?: unknown };
56+
if (typeof snapshot.taskIdentifier === "string") {
57+
taskIdentifier = snapshot.taskIdentifier;
58+
}
59+
} catch {
60+
// Fall through — taskIdentifier stays undefined.
61+
}
62+
const placeholderParts = [
63+
entry.createdAt.toISOString(),
64+
...(taskIdentifier ? [taskIdentifier] : []),
65+
"INFO",
66+
"Run is queued, has not started executing yet — no logs to download.",
67+
];
68+
const placeholder = placeholderParts.join(" ") + "\n";
69+
const placeholderReadable = new Readable({
70+
read() {
71+
this.push(placeholder);
72+
this.push(null);
73+
},
74+
});
75+
const gzipStream = createGzip();
76+
const compressed = placeholderReadable.pipe(gzipStream);
77+
return new Response(compressed as any, {
78+
status: 200,
79+
headers: {
80+
"Content-Type": "application/octet-stream",
81+
"Content-Disposition": `attachment; filename="${parsedParams.runParam}.log"`,
82+
"Content-Encoding": "gzip",
83+
},
84+
});
85+
}
86+
}
87+
}
3388
return new Response("Not found", { status: 404 });
3489
}
3590

0 commit comments

Comments
 (0)