Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit 5e38056

Browse files
fix(core): harden archived task hydration
Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22
1 parent fb12c62 commit 5e38056

2 files changed

Lines changed: 62 additions & 3 deletions

File tree

‎packages/core/src/cloud-task/cloud-task-engine.ts‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const SSE_HEALTHY_CONNECTION_MS = 60_000;
3434
const EVENT_BATCH_FLUSH_MS = 16;
3535
const EVENT_BATCH_MAX_SIZE = 50;
3636
const SESSION_LOG_PAGE_LIMIT = 5_000;
37+
const ARCHIVED_LOG_FETCH_TIMEOUT_MS = 15_000;
3738
const MAX_HANDLED_RELAY_REQUEST_IDS = 1_000;
3839
const MCP_RELAY_METHODS_WITHOUT_APPROVAL = new Set([
3940
"initialize",
@@ -2162,16 +2163,24 @@ export class CloudTaskEngine extends TypedEventEmitter<CloudTaskEvents> {
21622163
logUrl: string,
21632164
): Promise<StoredLogEntry[] | null> {
21642165
try {
2165-
const response = await this.streamFetch(logUrl);
2166+
const response = await this.streamFetch(logUrl, {
2167+
signal: AbortSignal.timeout(ARCHIVED_LOG_FETCH_TIMEOUT_MS),
2168+
});
21662169
if (!response.ok) return null;
21672170
const content = await response.text();
21682171
if (!content.trim()) return [];
21692172
return content
21702173
.trim()
21712174
.split("\n")
2172-
.map((line) => JSON.parse(line) as StoredLogEntry);
2175+
.flatMap((line) => {
2176+
try {
2177+
return [JSON.parse(line) as StoredLogEntry];
2178+
} catch {
2179+
return [];
2180+
}
2181+
});
21732182
} catch (error) {
2174-
this.log.warn("Cloud task archived logs fetch error", { logUrl, error });
2183+
this.log.warn("Cloud task archived logs fetch error", { error });
21752184
return null;
21762185
}
21772186
}

‎packages/core/src/cloud-task/cloud-task.test.ts‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2578,6 +2578,56 @@ describe("CloudTaskEngine", () => {
25782578
status: "completed",
25792579
}),
25802580
);
2581+
expect(
2582+
mockNetFetch.mock.calls.find(
2583+
([input]) => input === "https://logs.example.com/run-1.jsonl",
2584+
)?.[1]?.signal,
2585+
).toBeInstanceOf(AbortSignal);
2586+
});
2587+
2588+
it("keeps valid archived entries around malformed lines", async () => {
2589+
const updates: unknown[] = [];
2590+
service.on(CloudTaskEvent.Update, (payload) => updates.push(payload));
2591+
const archivedEntry = {
2592+
type: "notification",
2593+
timestamp: "2026-01-01T00:00:00Z",
2594+
};
2595+
2596+
mockNetFetch.mockImplementation((input: string | Request) => {
2597+
const url = typeof input === "string" ? input : input.url;
2598+
if (url.includes("/session_logs/")) {
2599+
return Promise.resolve(
2600+
createJsonResponse([], 200, { "X-Has-More": "false" }),
2601+
);
2602+
}
2603+
if (url === "https://logs.example.com/run-1.jsonl") {
2604+
return Promise.resolve(
2605+
new Response(`invalid\n${JSON.stringify(archivedEntry)}\n`, {
2606+
status: 200,
2607+
}),
2608+
);
2609+
}
2610+
return Promise.resolve(
2611+
createJsonResponse({
2612+
id: "run-1",
2613+
status: "completed",
2614+
log_url: "https://logs.example.com/run-1.jsonl",
2615+
updated_at: "2026-01-01T00:00:00Z",
2616+
}),
2617+
);
2618+
});
2619+
2620+
service.watch({
2621+
taskId: "task-1",
2622+
runId: "run-1",
2623+
apiHost: "https://app.example.com",
2624+
teamId: 2,
2625+
});
2626+
2627+
await waitFor(() => updates.length === 1);
2628+
expect(updates[0]).toEqual(
2629+
expect.objectContaining({ newEntries: [archivedEntry] }),
2630+
);
25812631
});
25822632

25832633
const guardedFetchStatusExpectations = [

0 commit comments

Comments
 (0)