bug: batchTrigger returns stale failed runs instead of re-triggering when idempotency key points to a dead run
維護者通常 1 天內回覆
還沒有人認領這個 Issue。
評估
- 難度
- 3/5
- 預估耗時
- 1-2 天
- 新手友好度
- 30/100
- Issue 類型
- 缺陷
- 描述清晰度
- 描述清楚
- 活躍度
- 停滯
- 技術堆疊
- postgresql, typescript
- 領域
- backend
研究方向
從 apps/webapp/app/v3/services/batchTriggerV3.server.ts 中的 BatchTriggerV3Service.#prepareRunData(約第 452-469 行)開始,並與 apps/webapp/app/runEngine/concerns/idempotencyKeys.server.ts(約第 463 行)中的 IdempotencyKeyConcern.handleExistingRun 進行比較,後者已經呼叫了 shouldIdempotencyKeyBeCleared(status)。接著追蹤 internal-packages/run-store/src/types.ts 中的 IdempotencyKeyRunMatch 以及 internal-packages/run-store/src/PostgresRunStore.ts 中的 findRunsByIdempotencyKeys,後者在 SELECT 中缺少 status。完成的標誌是 batchTrigger 回傳 isCached:false,並為終態失敗狀態建立新的 run,這已透過 issue 中的重現方式驗證;注意 PR #4912 與 #4913 已被關閉且未合併,因此在開始前請先查明原因。
由索引模型根據 Issue 內容生成。
描述
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
- 主要語言
- TypeScript
- 星號
- 16.5k
- 分支
- 1.5k
- 平均合併
- 1 天 15 小時
- 30 天內合併 PR
- 17
環境準備
這個專案沒有提供開發容器、Dockerfile 或貢獻指南,環境需要你自己搭建:先看它的 README,通用步驟見我們的新手貢獻指南。
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
triggerdotdev/trigger.dev 的其他 Issue
-
bug: retry.fetch ignores the caller's abort signal and retries aborted requests可能重新可做 關聯的 PR 已關閉且未合併。 未關閉
難度 2/5 1-3 小時 新手友好度 76/100
triggerdotdev/trigger.dev#5116 · 2 則留言 ·
維護者通常 1 天內回覆
-
bug: Chat transport crashes on Safari < 17.4: AbortSignal.any is not a function可能重新可做 關聯的 PR 已關閉且未合併。 未關閉
難度 2/5 1-3 小時 新手友好度 83/100
triggerdotdev/trigger.dev#5098 · 2 則留言 ·
維護者通常 1 天內回覆
-
bug: Side menu items lose their active state on detail pages可能重新可做 關聯的 PR 已關閉且未合併。 未關閉
難度 2/5 1-3 小時 新手友好度 72/100
triggerdotdev/trigger.dev#5088 ·
維護者通常 1 天內回覆
-
bug: RBAC loader logs "plugin found but failed to load" when @triggerdotdev/plugins is simply not installed可能重新可做 @itsfaryal 於 17 天前認領,目前沒有進行中的 PR。 未關閉
難度 2/5 1-3 小時 新手友好度 82/100
triggerdotdev/trigger.dev#5083 · 1 則留言 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 83/100
triggerdotdev/trigger.dev#5072 · 1 則留言 ·
維護者通常 1 天內回覆
查看 triggerdotdev/trigger.dev 的全部 Issue
相似的 Issue
-
難度 2/5 1-3 小時 新手友好度 72/100
維護者通常 1 天內回覆
-
bug platform-android
難度 2/5 1-3 小時 新手友好度 68/100
Termix-SSH/Mobile#146 · 1 則留言 ·
維護者通常 2 天內回覆
-
bug
難度 2/5 1-3 小時 新手友好度 66/100
devswha/herdr-web-ui#3875 ·
-
難度 1/5 1 小時以內 新手友好度 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
難度 2/5 1-3 小時 新手友好度 78/100
維護者通常 1 天內回覆