Skip to content

fix(batch): clear idempotency keys pointing at dead runs in batchTrigger - #4912

Closed
breken-ai wants to merge 1 commit into
triggerdotdev:mainfrom
breken-ai:fix-4819-batch-idempotency-dead-runs
Closed

fix(batch): clear idempotency keys pointing at dead runs in batchTrigger#4912
breken-ai wants to merge 1 commit into
triggerdotdev:mainfrom
breken-ai:fix-4819-batch-idempotency-dead-runs

Conversation

@breken-ai

Copy link
Copy Markdown

Fixes #4819.

The single-trigger path already guards this: IdempotencyKeyConcern.handleExistingRun clears the key and mints a fresh run when shouldIdempotencyKeyBeCleared(existingRun.status) holds (failed statuses + EXPIRED). The batch path (batchTriggerV3.server.ts:453) only tested time expiry, so batchTrigger with an idempotency key pointing at a dead run returned that FAILED run as isCached: true - and kept returning it on every retry.

Three changes, following the shape @itzzdev09 sketched in the issue thread:

  1. internal-packages/run-store/src/PostgresRunStore.ts - findRunsByIdempotencyKeys now selects the status column (the wrinkle he flagged: cachedRun previously had no status to check). delegatingRunStore / runOpsStore forward unchanged.
  2. internal-packages/run-store/src/types.ts - IdempotencyKeyRunMatch gains status: TaskRunStatus.
  3. apps/webapp/app/v3/services/batchTriggerV3.server.ts - the cached-run check becomes timeExpired || shouldIdempotencyKeyBeCleared(cachedRun.status) in the same branch, so the key is cleared and a fresh run is minted, matching single-trigger behavior. Policy stays owned by the webapp; the store just returns one more column.

Credit: @Jaimin2687 for the report, @itzzdev09 for the analysis and fix sketch in #4819.

Verification (honest status):

  • SQL level against a real Postgres 14 cluster: pre-patch query returns no status; post-patch query (exact text, UNION ALL with per-branch params) returns status correctly for CRASHED/PENDING rows.
  • Decision logic red/green with the repo's own constants (taskStatus.ts): all 6 clearable statuses (INTERRUPTED, COMPLETED_WITH_ERRORS, SYSTEM_FAILURE, CRASHED, TIMED_OUT, EXPIRED) flip from stale-cache to clear-and-fresh-run; COMPLETED_SUCCESSFULLY / EXECUTING unchanged; time-expiry path unchanged.
  • A store-level regression test is included (PostgresRunStore.findRunsByIdempotencyKeys.test.ts). The repo's test harness requires Docker/testcontainers, which my environment does not have, so it was not run locally - it should run in CI.

Built by breken, your AI support engineer - breken.ai - this one's on us.

The single-trigger path already clears an idempotency key when the
cached run reached a clearable terminal state
(shouldIdempotencyKeyBeCleared: failed statuses + EXPIRED). The batch
path only tested time expiry, so batchTrigger with an idempotency key
pointing at a FAILED run returned the dead run as isCached forever.

- PostgresRunStore.findRunsByIdempotencyKeys now selects the status
  column
- IdempotencyKeyRunMatch gains status
- batchTriggerV3 clears the key and mints a fresh run when
  shouldIdempotencyKeyBeCleared(status) holds, same branch as time
  expiry

Credit: reported by @Jaimin2687 in triggerdotdev#4819; fix shape follows
@itzzdev09's analysis in the issue thread.
@changeset-bot

changeset-bot Bot commented Sep 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0301aef

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: ad4507f3-36fc-4351-b0fb-eb66419f118d

📥 Commits

Reviewing files that changed from the base of the PR and between 6f5c49c and 0301aef.

📒 Files selected for processing (4)
  • apps/webapp/app/v3/services/batchTriggerV3.server.ts
  • internal-packages/run-store/src/PostgresRunStore.findRunsByIdempotencyKeys.test.ts
  • internal-packages/run-store/src/PostgresRunStore.ts
  • internal-packages/run-store/src/types.ts

Walkthrough

The run-store idempotency lookup now returns each matched run’s status. The batch trigger uses shouldIdempotencyKeyBeCleared when it evaluates cached runs. If a cached run has a clearable terminal status, the batch trigger clears the key and creates a fresh run instead of returning the failed run as cached. A PostgreSQL test verifies returned statuses.

Severity of issue fixed: Medium

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Hi @breken-ai, thanks for your interest in contributing!

This project requires that pull request authors are vouched, and you are not in the list of vouched users.

This PR will be closed automatically. See https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md for more details.

@github-actions github-actions Bot closed this Sep 9, 2026

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

// isCached - clear the key and mint a fresh run, same as a time-expired one.
if (
(cachedRun.idempotencyKeyExpiresAt && cachedRun.idempotencyKeyExpiresAt < new Date()) ||
shouldIdempotencyKeyBeCleared(cachedRun.status)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Late revived runs are skipped

When a failed cached run appears late in a parallel batch, shouldIdempotencyKeyBeCleared marks it new. Parallel ranges cover only newRunCount positions in the full item list. The batch returns a minted run ID that never executes.

Prompt for agents
The new failed-status branch in apps/webapp/app/v3/services/batchTriggerV3.server.ts can turn an item anywhere in runs into a fresh run. In #createAndProcessBatchTaskRun, parallel ranges are sized from newRunCount, but #processBatchTaskRunItems applies those ranges to the complete batch.runIds array, where cached and fresh entries remain interleaved. A fresh item after the covered prefix is never processed. Make parallel processing cover every position that can contain a fresh run, or preserve enough metadata to schedule exactly the fresh positions. Add a regression test with more than the async threshold, mostly cached items, and a failed cached item after the first computed range.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +453 to +458
// Parity with the single-trigger path (IdempotencyKeyConcern.handleExistingRun): a cached
// run that reached a clearable terminal state (failed/expired) must not be returned as
// isCached - clear the key and mint a fresh run, same as a time-expired one.
if (
(cachedRun.idempotencyKeyExpiresAt && cachedRun.idempotencyKeyExpiresAt < new Date()) ||
shouldIdempotencyKeyBeCleared(cachedRun.status)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 User-facing fix lacks release note

This server fix changes visible batch retry behavior. Repository rules require a .server-changes/ entry for user-facing server fixes.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant