Problem
File: apps/web/src/lib/actions/settings.ts — cancelGuardJobsForOrg
const res = await fetch(`${TRIGGER_BASE}/api/v3/runs?${qs}`, { headers });
Issue 1 — No AbortSignal/timeout: All 16 Trigger.dev fetch calls (4 tasks × 4 statuses) have no timeout. If the Trigger.dev API is slow or unresponsive, the Server Action that triggered this (plan update / product removal) stalls indefinitely — blocking the admin's response.
Issue 2 — Fetches first page only, no pagination: The /api/v3/runs endpoint is paginated. The code only reads body.data from the first page. An org with many queued runs (e.g., a large NRD feed fan-out spike) will have subsequent pages that are never fetched and never cancelled. When the Guard subscription lapses those runs continue executing against resources the org no longer pays for (#247).
Issue 3 — Runs filtered in JS, not by API: The code fetches ALL runs for a task/status globally, then filters by metadata.org_id in JavaScript. This is inefficient and means the first-page truncation issue in point 2 is worse — the API may return 100 runs for other orgs before the target org's runs appear.
Fix
- Add
signal: AbortSignal.timeout(10_000) to each fetch call.
- Either use the Trigger.dev SDK (already a dep in
@repo/jobs) to cancel by tag/metadata server-side, or loop through pages until next_page is null.
- If the Trigger API supports filtering by metadata, pass
metadata[org_id]=${orgId} as a query param to reduce client-side filtering.
Problem
File:
apps/web/src/lib/actions/settings.ts—cancelGuardJobsForOrgIssue 1 — No AbortSignal/timeout: All 16 Trigger.dev fetch calls (4 tasks × 4 statuses) have no timeout. If the Trigger.dev API is slow or unresponsive, the Server Action that triggered this (plan update / product removal) stalls indefinitely — blocking the admin's response.
Issue 2 — Fetches first page only, no pagination: The
/api/v3/runsendpoint is paginated. The code only readsbody.datafrom the first page. An org with many queued runs (e.g., a large NRD feed fan-out spike) will have subsequent pages that are never fetched and never cancelled. When the Guard subscription lapses those runs continue executing against resources the org no longer pays for (#247).Issue 3 — Runs filtered in JS, not by API: The code fetches ALL runs for a task/status globally, then filters by
metadata.org_idin JavaScript. This is inefficient and means the first-page truncation issue in point 2 is worse — the API may return 100 runs for other orgs before the target org's runs appear.Fix
signal: AbortSignal.timeout(10_000)to each fetch call.@repo/jobs) to cancel by tag/metadata server-side, or loop through pages untilnext_pageis null.metadata[org_id]=${orgId}as a query param to reduce client-side filtering.