bug: batchTrigger returns stale failed runs instead of re-triggering when idempotency key points to a dead run
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 30/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Đình trệ
- Công nghệ
- postgresql, typescript
- Lĩnh vực
- backend
Hướng nghiên cứu
Bắt đầu từ BatchTriggerV3Service.#prepareRunData trong apps/webapp/app/v3/services/batchTriggerV3.server.ts (khoảng các dòng 452-469) và so sánh với IdempotencyKeyConcern.handleExistingRun trong apps/webapp/app/runEngine/concerns/idempotencyKeys.server.ts (~dòng 463), nơi đã gọi shouldIdempotencyKeyBeCleared(status). Sau đó, lần theo IdempotencyKeyRunMatch trong internal-packages/run-store/src/types.ts và findRunsByIdempotencyKeys trong internal-packages/run-store/src/PostgresRunStore.ts, nơi status bị thiếu trong SELECT. Hoàn thành nghĩa là batchTrigger trả về isCached:false và tạo một run mới cho các trạng thái lỗi terminal, đã được xác minh bằng cách tái hiện trong issue; lưu ý rằng PR #4912 và #4913 đã bị đóng mà không merge, vì vậy hãy kiểm tra lý do trước khi bắt đầu.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Bug Description
batchTrigger and single trigger behave differently when an idempotency key points to a run that already failed.
With single trigger, if a previous run with the same idempotency key ended up in a terminal failure state (CRASHED, SYSTEM_FAILURE, TIMED_OUT, EXPIRED, COMPLETED_WITH_ERRORS, INTERRUPTED), it correctly clears the key and creates a fresh run. This works because IdempotencyKeyConcern.handleExistingRun calls shouldIdempotencyKeyBeCleared(status) before deciding whether to return a cached result.
With batchTrigger, that same check is missing. The batch path in BatchTriggerV3Service.#prepareRunData only checks time-based expiration (idempotencyKeyExpiresAt < now), so a failed run gets returned as isCached: true — silently handing back a dead run that will never produce output.
Steps to Reproduce
// 1. A task that always fails
export const failingTask = task({
id: "failing-task",
run: async () => { throw new Error("boom"); },
});
// 2. Batch trigger with idempotency key
await tasks.batchTrigger("failing-task", [
{ payload: {}, options: { idempotencyKey: "key-1" } },
]);
// Wait for the run to reach CRASHED / COMPLETED_WITH_ERRORS...
// 3. Trigger again with the same key
await tasks.batchTrigger("failing-task", [
{ payload: {}, options: { idempotencyKey: "key-1" } },
]);
// Expected: isCached: false (fresh run created)
// Actual: isCached: true (dead run returned)
Compare with single trigger — this already works correctly:
await tasks.trigger("failing-task", {}, { idempotencyKey: "key-2" });
// Wait for failure...
await tasks.trigger("failing-task", {}, { idempotencyKey: "key-2" });
// Correctly returns isCached: false and creates a new run
Root Cause
The SQL query in PostgresRunStore.findRunsByIdempotencyKeys never selects the status column, and the IdempotencyKeyRunMatch type doesn't include it. So #prepareRunData has no way to check whether a matched run is in a clearable failure state — it can only check time-based expiration.
The single-trigger path works because IdempotencyKeyConcern.handleExistingRun fetches the full run record and calls shouldIdempotencyKeyBeCleared(status) at idempotencyKeys.server.ts:463. The batch path at batchTriggerV3.server.ts:452-469 skips this entirely.
| Check | Single trigger | Batch trigger |
|---|---|---|
| Time-based expiry | ✅ idempotencyKeyExpiresAt < now |
✅ idempotencyKeyExpiresAt < now |
| Failure status check | ✅ shouldIdempotencyKeyBeCleared(status) |
❌ Missing |
Affected Statuses
These statuses should clear the idempotency key and re-trigger (works for single trigger, broken for batch):
CRASHEDSYSTEM_FAILURETIMED_OUTEXPIREDCOMPLETED_WITH_ERRORSINTERRUPTED
Suggested Fix
I already wrote and tested a patch for this. My PR #4818 was auto-closed since I'm not a vouched contributor, but the fix is ready on my fork if you'd like to pull it in or use it as reference:
Branch: Jaimin2687:fix/batch-trigger-idempotency-key-status-check
The fix is three small changes:
internal-packages/run-store/src/types.ts— Addstatus: stringtoIdempotencyKeyRunMatchinternal-packages/run-store/src/PostgresRunStore.ts— Add"status"to the raw SQL SELECT infindRunsByIdempotencyKeysapps/webapp/app/v3/services/batchTriggerV3.server.ts— ImportshouldIdempotencyKeyBeClearedand add the guard after the expiry check in#prepareRunData, mirroring the single-trigger path
The branch also includes 21 unit tests and 2 integration tests (testcontainers) covering all TaskRunStatus values. Everything passes typecheck, build, format, and lint.
Environment
- trigger.dev
mainbranch (as of Aug 28, 2025) - Affects all environments (dev, staging, production)
- Backend only, no UI impact
- Ngôn ngữ chính
- TypeScript
- Star
- 16.5k
- Fork
- 1.5k
- Merge trung bình
- 1 ngày 15 giờ
- Pull request đã merge (30 ngày)
- 17
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của triggerdotdev/trigger.dev
-
bug: retry.fetch ignores the caller's abort signal and retries aborted requestsCó thể làm lại được Pull request cho issue này đã bị đóng mà không được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
triggerdotdev/trigger.dev#5116 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug: Chat transport crashes on Safari < 17.4: AbortSignal.any is not a functionCó thể làm lại được Pull request cho issue này đã bị đóng mà không được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 83/100
triggerdotdev/trigger.dev#5098 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug: Side menu items lose their active state on detail pagesCó thể làm lại được Pull request cho issue này đã bị đóng mà không được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
triggerdotdev/trigger.dev#5088 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug: RBAC loader logs "plugin found but failed to load" when @triggerdotdev/plugins is simply not installedCó thể làm lại được @itsfaryal đã nhận 17 ngày trước và không có pull request nào đang mở. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
triggerdotdev/trigger.dev#5083 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 83/100
triggerdotdev/trigger.dev#5072 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của triggerdotdev/trigger.dev
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug platform-android
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Termix-SSH/Mobile#146 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
devswha/herdr-web-ui#3875 ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
-
area: runtime chore cli consistency dependencies developer-experience difficulty: easy discovery documentation enhancement good first issue help wanted hygiene json-output packaging priority: P3 tests version
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày