feat: add list mode to GET /api/tasks/runs#245
Conversation
Make runId optional. When omitted, returns recent runs for the authenticated account by querying Trigger.dev runs tagged with account:<accountId>. Add optional limit parameter (default 20, max 100). - Update validateGetTaskRunQuery to return discriminated union - Add listTaskRuns function using runs.list with tag filter - Branch handler logic on retrieve vs list mode - Add tests for list mode (16 total tests passing) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds a list mode to the task-run endpoint, introduces listTaskRuns and a date utility, refactors retrieveTaskRun to return a uniform TaskRun shape, and updates query validation to a discriminated union supporting "retrieve" and "list" modes. Changes
Sequence DiagramsequenceDiagram
participant Client
participant Handler as API Handler
participant Validator as Query Validator
participant ListService as listTaskRuns
participant TriggerAPI as Trigger.dev API
participant Retriever as retrieveTaskRun
Client->>Handler: GET /task-runs?[runId | ?limit]
Handler->>Validator: Validate query params
alt List Mode (no runId)
Validator-->>Handler: { mode: "list", accountId, limit }
Handler->>ListService: listTaskRuns(accountId, limit)
ListService->>TriggerAPI: runs.list(tag=account:accountId, limit)
TriggerAPI-->>ListService: Raw task runs
ListService-->>ListService: Normalize dates (toISOStringOrNull) & map to TaskRun
ListService-->>Handler: TaskRun[]
Handler-->>Client: { status: "success", runs: [...] } (200 + CORS)
else Retrieve Mode (with runId)
Validator-->>Handler: { mode: "retrieve", runId }
Handler->>Retriever: retrieveTaskRun(runId)
Retriever->>TriggerAPI: runs.get(runId)
TriggerAPI-->>Retriever: Raw task run
Retriever-->>Handler: TaskRun | null
Handler-->>Client: { status: "success", runs: [TaskRun] } (200) or 404/500
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 1✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
listTaskRuns now maps Trigger.dev statuses to pending/complete/failed matching the same shape as retrieveTaskRun, with an added id field. Removes the separate TaskRunListItem type. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
lib/tasks/getTaskRunHandler.ts (1)
51-51:⚠️ Potential issue | 🟡 MinorUpdate catch log text to match both retrieve and list flows.
Line 51 says “retrieving” even when list mode fails, which makes ops debugging noisier.
🔧 Suggested fix
- console.error("Error retrieving task run:", error); + console.error("Error handling task run request:", error);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/tasks/getTaskRunHandler.ts` at line 51, The catch log in getTaskRunHandler.ts currently uses "Error retrieving task run:" which is misleading for the list flow; update the error message logged in the catch block (where console.error("Error retrieving task run:", error) is called) to a neutral message that covers both operations—e.g., "Error retrieving or listing task run(s):"—so both the retrieve and list flows produce an accurate, unified log entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/tasks/validateGetTaskRunQuery.ts`:
- Around line 8-12: The runId schema in validateGetTaskRunQuery.ts trims after
length validation, allowing whitespace-only values to pass .min(1) then become
empty and trigger list-mode; change the schema so trimming happens before length
checks (e.g., apply .transform(val => val.trim()) or .trim() first, then .min(1)
or use a .refine on the trimmed value) to ensure whitespace-only runId returns a
400, and apply the same ordering/fix to the similar schema block referenced
around lines 63-67 to validate trimmed input before checking length.
- Around line 7-23: Export the Zod schema and add an inferred TypeScript type
from it: make getTaskRunQuerySchema exported (export const getTaskRunQuerySchema
= ...) and add an exported inferred type using z.infer (e.g., export type
InferredGetTaskRunQuery = z.infer<typeof getTaskRunQuerySchema>), leaving the
existing ValidatedRetrieveQuery, ValidatedListQuery and GetTaskRunQuery types
intact so callers can use either the raw schema type or the existing validated
union types.
In `@lib/trigger/listTaskRuns.ts`:
- Line 43: Replace the unsafe type assertion for metadata with a runtime guard
in listTaskRuns: instead of (run.metadata as Record<string, unknown>) ?? null,
check that run.metadata is a non-null plain object (typeof run.metadata ===
'object' && run.metadata !== null && !Array.isArray(run.metadata)) and only then
assign it to the metadata property; otherwise set metadata to null. Update the
metadata assignment in the object returned by the function (the place
referencing run.metadata) so the response shape cannot receive
strings/numbers/arrays.
---
Outside diff comments:
In `@lib/tasks/getTaskRunHandler.ts`:
- Line 51: The catch log in getTaskRunHandler.ts currently uses "Error
retrieving task run:" which is misleading for the list flow; update the error
message logged in the catch block (where console.error("Error retrieving task
run:", error) is called) to a neutral message that covers both operations—e.g.,
"Error retrieving or listing task run(s):"—so both the retrieve and list flows
produce an accurate, unified log entry.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
lib/tasks/__tests__/getTaskRunHandler.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**lib/trigger/__tests__/listTaskRuns.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**
📒 Files selected for processing (3)
lib/tasks/getTaskRunHandler.tslib/tasks/validateGetTaskRunQuery.tslib/trigger/listTaskRuns.ts
| const getTaskRunQuerySchema = z.object({ | ||
| runId: z | ||
| .string({ message: "runId is required" }) | ||
| .min(1, "runId is required") | ||
| .transform(val => val.trim()), | ||
| .string() | ||
| .min(1) | ||
| .transform(val => val.trim()) | ||
| .optional(), | ||
| limit: z.coerce | ||
| .number() | ||
| .int() | ||
| .min(1) | ||
| .max(100) | ||
| .default(20), | ||
| }); | ||
|
|
||
| export type GetTaskRunQuery = z.infer<typeof getTaskRunQuerySchema>; | ||
| export type ValidatedRetrieveQuery = { mode: "retrieve"; runId: string }; | ||
| export type ValidatedListQuery = { mode: "list"; accountId: string; limit: number }; | ||
| export type GetTaskRunQuery = ValidatedRetrieveQuery | ValidatedListQuery; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Export the validation schema and an inferred schema type from this validate module.
This file currently exports validated union types, but not the schema itself nor an inferred schema type.
♻️ Suggested change
-const getTaskRunQuerySchema = z.object({
+export const getTaskRunQuerySchema = z.object({
...
});
+
+export type GetTaskRunQuerySchema = z.infer<typeof getTaskRunQuerySchema>;As per coding guidelines: lib/**/validate*.ts: “Create validate functions ... files that export both the schema and inferred TypeScript type”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/tasks/validateGetTaskRunQuery.ts` around lines 7 - 23, Export the Zod
schema and add an inferred TypeScript type from it: make getTaskRunQuerySchema
exported (export const getTaskRunQuerySchema = ...) and add an exported inferred
type using z.infer (e.g., export type InferredGetTaskRunQuery = z.infer<typeof
getTaskRunQuerySchema>), leaving the existing ValidatedRetrieveQuery,
ValidatedListQuery and GetTaskRunQuery types intact so callers can use either
the raw schema type or the existing validated union types.
| runId: z | ||
| .string({ message: "runId is required" }) | ||
| .min(1, "runId is required") | ||
| .transform(val => val.trim()), | ||
| .string() | ||
| .min(1) | ||
| .transform(val => val.trim()) | ||
| .optional(), |
There was a problem hiding this comment.
Trim runId before length validation to prevent accidental list-mode fallback.
At Line 10-11, whitespace-only runId (e.g. " ") passes .min(1) and is then transformed to ""; at Line 63 this falls through to list mode. That should be a 400, not a mode switch.
🔧 Suggested fix
const getTaskRunQuerySchema = z.object({
runId: z
.string()
- .min(1)
- .transform(val => val.trim())
+ .trim()
+ .min(1)
.optional(),
@@
- if (result.data.runId) {
+ if (result.data.runId !== undefined) {
return { mode: "retrieve", runId: result.data.runId };
}Also applies to: 63-67
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/tasks/validateGetTaskRunQuery.ts` around lines 8 - 12, The runId schema
in validateGetTaskRunQuery.ts trims after length validation, allowing
whitespace-only values to pass .min(1) then become empty and trigger list-mode;
change the schema so trimming happens before length checks (e.g., apply
.transform(val => val.trim()) or .trim() first, then .min(1) or use a .refine on
the trimmed value) to ensure whitespace-only runId returns a 400, and apply the
same ordering/fix to the similar schema block referenced around lines 63-67 to
validate trimmed input before checking length.
| return result.data.map(run => { | ||
| const common = { | ||
| id: run.id, | ||
| metadata: (run.metadata as Record<string, unknown>) ?? null, |
There was a problem hiding this comment.
Guard metadata at runtime instead of relying on a type assertion.
At Line 43, the cast can pass through non-object values (e.g., string/number) and break the declared response shape.
🔧 Suggested fix
- metadata: (run.metadata as Record<string, unknown>) ?? null,
+ metadata:
+ run.metadata && typeof run.metadata === "object" && !Array.isArray(run.metadata)
+ ? (run.metadata as Record<string, unknown>)
+ : null,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| metadata: (run.metadata as Record<string, unknown>) ?? null, | |
| metadata: | |
| run.metadata && typeof run.metadata === "object" && !Array.isArray(run.metadata) | |
| ? (run.metadata as Record<string, unknown>) | |
| : null, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/trigger/listTaskRuns.ts` at line 43, Replace the unsafe type assertion
for metadata with a runtime guard in listTaskRuns: instead of (run.metadata as
Record<string, unknown>) ?? null, check that run.metadata is a non-null plain
object (typeof run.metadata === 'object' && run.metadata !== null &&
!Array.isArray(run.metadata)) and only then assign it to the metadata property;
otherwise set metadata to null. Update the metadata assignment in the object
returned by the function (the place referencing run.metadata) so the response
shape cannot receive strings/numbers/arrays.
Both retrieve and list modes now return the same response shape:
{ status: "success", runs: TaskRunResult[] }. When runId is provided,
runs is a single-element array. This eliminates the oneOf in the
response schema.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add id field to retrieveTaskRun return type - Update validateGetTaskRunQuery tests for mode-based return type - Treat empty runId as list mode (not validation error) - Update retrieveTaskRun tests with id in expectedCommon Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Trigger.dev runs.list() API does not include output on list items (only runs.retrieve() does). Set data to null for completed runs in list mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SRP — move shared date conversion utility out of listTaskRuns and use it in both listTaskRuns and retrieveTaskRun. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Log the tag query, result count, and resolved accountId to debug empty array responses. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Return raw status strings (COMPLETED, FAILED, EXECUTING, etc.) from retrieveTaskRun and listTaskRuns instead of mapping to pending/complete/failed. This preserves all SDK status granularity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
lib/tasks/validateGetTaskRunQuery.ts (1)
7-23:⚠️ Potential issue | 🟠 MajorExport the validation schema and an inferred schema type from this module.
getTaskRunQuerySchemais still private, and there’s no exportedz.infertype from it. That makes schema reuse and type alignment harder in validate consumers/tests.♻️ Suggested change
-const getTaskRunQuerySchema = z.object({ +export const getTaskRunQuerySchema = z.object({ runId: z .string() .min(1) .transform(val => val.trim()) .optional(), limit: z.coerce .number() .int() .min(1) .max(100) .default(20), }); +export type GetTaskRunQuerySchema = z.infer<typeof getTaskRunQuerySchema>; export type ValidatedRetrieveQuery = { mode: "retrieve"; runId: string }; export type ValidatedListQuery = { mode: "list"; accountId: string; limit: number }; export type GetTaskRunQuery = ValidatedRetrieveQuery | ValidatedListQuery;As per coding guidelines:
lib/**/validate*.tsfiles must “export both the schema and inferred TypeScript type.”🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/tasks/validateGetTaskRunQuery.ts` around lines 7 - 23, Export the validation schema and its inferred type so other modules/tests can reuse them: make getTaskRunQuerySchema exported (export const getTaskRunQuerySchema) and add an exported inferred type e.g. export type GetTaskRunQuerySchema = z.infer<typeof getTaskRunQuerySchema>; keep existing explicit union types (ValidatedRetrieveQuery, ValidatedListQuery, GetTaskRunQuery) as-is for compatibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@lib/tasks/validateGetTaskRunQuery.ts`:
- Around line 7-23: Export the validation schema and its inferred type so other
modules/tests can reuse them: make getTaskRunQuerySchema exported (export const
getTaskRunQuerySchema) and add an exported inferred type e.g. export type
GetTaskRunQuerySchema = z.infer<typeof getTaskRunQuerySchema>; keep existing
explicit union types (ValidatedRetrieveQuery, ValidatedListQuery,
GetTaskRunQuery) as-is for compatibility.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
lib/tasks/__tests__/getTaskRunHandler.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**lib/tasks/__tests__/validateGetTaskRunQuery.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**lib/trigger/__tests__/listTaskRuns.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**lib/trigger/__tests__/retrieveTaskRun.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**
📒 Files selected for processing (5)
lib/tasks/getTaskRunHandler.tslib/tasks/validateGetTaskRunQuery.tslib/trigger/listTaskRuns.tslib/trigger/retrieveTaskRun.tslib/trigger/toISOStringOrNull.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- lib/trigger/listTaskRuns.ts
- lib/tasks/getTaskRunHandler.ts
Return Trigger.dev SDK objects directly from retrieveTaskRun and listTaskRuns instead of manually mapping fields. Removes the TaskRun interface, toISOStringOrNull utility, and all unnecessary transformation code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
runIdoptional inGET /api/tasks/runs— when omitted, returns recent runs for the authenticated accountruns.list()withaccount:<accountId>tag filterlimitquery parameter (default 20, max 100)Test plan
curl /api/tasks/runs(no runId, with auth) returns{ status: "success", runs: [...] }curl /api/tasks/runs?runId=<id>returns existing single-run behavior unchangedcurl /api/tasks/runs?limit=5returns at most 5 runspnpm test lib/tasks/__tests__/getTaskRunHandler.test.ts lib/trigger/__tests__/listTaskRuns.test.ts🤖 Generated with Claude Code
Summary by CodeRabbit