bug: batchTrigger returns stale failed runs instead of re-triggering when idempotency key points to a dead run

Aberta
#4,819 2 comentários 0 reações 0 responsáveis Ver no GitHub

Mantenedores costumam responder em até 1 dia

Ninguém assumiu esta issue ainda.

  • #4912 de @breken-ai — fechado sem integrar
  • #4913 de @itzzdev09 — fechado sem integrar
  • #5014 de @61465 — fechado sem integrar

Avaliação

Dificuldade
3/5
Tempo estimado
1-2 dias
Facilidade para iniciantes
30/100
Tipo de issue
Bug
Clareza
Claramente especificada
Status de atividade
Estagnada
Stack de tecnologia
postgresql, typescript
Domínio
backend

Direção de pesquisa

Comece em BatchTriggerV3Service.#prepareRunData em apps/webapp/app/v3/services/batchTriggerV3.server.ts (por volta das linhas 452-469) e compare com IdempotencyKeyConcern.handleExistingRun em apps/webapp/app/runEngine/concerns/idempotencyKeys.server.ts (~linha 463), que já chama shouldIdempotencyKeyBeCleared(status). Em seguida, rastreie IdempotencyKeyRunMatch em internal-packages/run-store/src/types.ts e findRunsByIdempotencyKeys em internal-packages/run-store/src/PostgresRunStore.ts, onde status está ausente no SELECT. Pronto significa que batchTrigger retorna isCached:false e cria um run novo para os status de falha terminal, verificado pela reprodução no issue; observe que os PRs #4912 e #4913 foram fechados sem merge, então verifique o motivo antes de começar.

Escrita pelo modelo de indexação a partir do texto da issue.

Descrição

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):

  • CRASHED
  • SYSTEM_FAILURE
  • TIMED_OUT
  • EXPIRED
  • COMPLETED_WITH_ERRORS
  • INTERRUPTED

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:

  1. internal-packages/run-store/src/types.ts — Add status: string to IdempotencyKeyRunMatch
  2. internal-packages/run-store/src/PostgresRunStore.ts — Add "status" to the raw SQL SELECT in findRunsByIdempotencyKeys
  3. apps/webapp/app/v3/services/batchTriggerV3.server.ts — Import shouldIdempotencyKeyBeCleared and 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 main branch (as of Aug 28, 2025)
  • Affects all environments (dev, staging, production)
  • Backend only, no UI impact
Linguagem predominante
TypeScript
Estrelas
16.5k
Forks
1.5k
Merge médio
1d 15h
PRs com merge (30d)
17

Preparar o ambiente

Este projeto não oferece contêiner de desenvolvimento, Dockerfile nem guia de contribuição, então a configuração fica por sua conta: comece pelo README e veja nosso guia da primeira contribuição para os passos gerais.

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de triggerdotdev/trigger.dev

Todas as issues de triggerdotdev/trigger.dev

Issues semelhantes

Mais issues de TypeScript

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.