bug: batchTrigger returns stale failed runs instead of re-triggering when idempotency key points to a dead run
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 3/5
- Temps estimé
- 1-2 jours
- Accessibilité débutants
- 30/100
- Type d'issue
- Bug
- Clarté
- Clairement spécifiée
- Activité
- À l'abandon
- Stack technique
- postgresql, typescript
- Domaine
- backend
Piste de recherche
Commence par BatchTriggerV3Service.#prepareRunData dans apps/webapp/app/v3/services/batchTriggerV3.server.ts (lignes environ 452-469) et compare-le à IdempotencyKeyConcern.handleExistingRun dans apps/webapp/app/runEngine/concerns/idempotencyKeys.server.ts (~ligne 463), qui appelle déjà shouldIdempotencyKeyBeCleared(status). Ensuite, suis IdempotencyKeyRunMatch dans internal-packages/run-store/src/types.ts et findRunsByIdempotencyKeys dans internal-packages/run-store/src/PostgresRunStore.ts, où status est absent du SELECT. Terminé signifie que batchTrigger renvoie isCached:false et crée un nouveau run pour les statuts d'échec terminal, vérifié par la reproduction décrite dans l'issue ; note que les PRs #4912 et #4913 ont été fermées sans être fusionnées, alors vérifie pourquoi avant de commencer.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
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
- Langage dominant
- TypeScript
- Étoiles
- 16.5k
- Forks
- 1.5k
- Merge moyen
- 1 j 15 h
- PR mergées (30 j)
- 17
Préparer son environnement
Ce projet ne fournit ni conteneur de développement, ni Dockerfile, ni guide de contribution : l'installation est à votre charge. Commencez par son README, et consultez notre guide de la première contribution pour les étapes générales.
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de triggerdotdev/trigger.dev
-
bug: retry.fetch ignores the caller's abort signal and retries aborted requestsPeut-être à nouveau libre Une pull request pour cette issue a été fermée sans être fusionnée. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
triggerdotdev/trigger.dev#5116 · 2 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
bug: Chat transport crashes on Safari < 17.4: AbortSignal.any is not a functionPeut-être à nouveau libre Une pull request pour cette issue a été fermée sans être fusionnée. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 83/100
triggerdotdev/trigger.dev#5098 · 2 commentaires ·
Les mainteneurs répondent en général sous 1 jour
-
bug: Side menu items lose their active state on detail pagesPeut-être à nouveau libre Une pull request pour cette issue a été fermée sans être fusionnée. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
triggerdotdev/trigger.dev#5088 ·
Les mainteneurs répondent en général sous 1 jour
-
bug: RBAC loader logs "plugin found but failed to load" when @triggerdotdev/plugins is simply not installedPeut-être à nouveau libre @itsfaryal l’a pris il y a 17 jours, et aucune pull request n’est ouverte. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
triggerdotdev/trigger.dev#5083 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 83/100
triggerdotdev/trigger.dev#5072 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de triggerdotdev/trigger.dev
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
Les mainteneurs répondent en général sous 1 jour
-
bug platform-android
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
Termix-SSH/Mobile#146 · 1 commentaire ·
Les mainteneurs répondent en général sous 2 jours
-
bug
Difficulté 2/5 1-3 heures Accessibilité débutants 66/100
devswha/herdr-web-ui#3875 ·
-
Difficulté 1/5 Moins d'une heure Accessibilité débutants 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
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour