Skip to content

Conversation

@mpcgrid
Copy link
Collaborator

@mpcgrid mpcgrid commented Dec 11, 2025

Add support for resetting idempotency keys both from ui and sdk

✅ Checklist

  • I have followed every step in the contributing guide
  • The PR title follows the convention.
  • I ran and tested the code works

Testing

  • Created a new run with a idempotency idempotencyKey.
  • Started a new run with the same task and got redirected to the first run.
  • Deleted the key from the UI on the run details
  • Started a new run with the same task and it created a new one
  • Did the above steps using the SDK

Changelog

  • Add new action route for resetting idempotency keys via UI
  • Add reset button in Idempotency section of run detail view
  • Added API and SDK for resetting imdepotency
  • Updated docs page for this feature

Screenshots

[Screenshots]
Screenshot 2025-12-11 at 11 56 37

- Add new action route for resetting idempotency keys via UI
- Add reset button in Idempotency section of run detail view
- Added API and SDK for resetting imdepotency
- Updated docs page for this feature
@changeset-bot
Copy link

changeset-bot bot commented Dec 11, 2025

⚠️ No Changeset found

Latest commit: 0210604

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
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Walkthrough

This pull request adds a comprehensive idempotency key reset feature across the application stack. It introduces two new API routes (one for direct API use, one for resource-based use), a service layer that queries and updates task runs in the database, UI components featuring a reset button and success notification, path builders, and SDK-level exports. The feature is also documented and includes corresponding response schemas and client methods.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Areas requiring additional attention:

  • apps/webapp/app/v3/services/resetIdempotencyKey.server.ts — Database query and bulk update logic; verify filtering by idempotencyKey, taskIdentifier, and environment ID is correct and does not affect unintended records
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx — Multiple validation checkpoints (run existence, idempotency key presence, taskIdentifier match, environment retrieval); ensure error messages and HTTP status codes are appropriate for each failure path
  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts — Verify authorization scopes (write action and super scopes) are correctly enforced and appropriate for this destructive operation
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx — Fetcher integration and toast success handling; confirm the reset action response structure matches expected payload and UI state transitions are correct

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding support for resetting idempotency keys, which aligns with all the file modifications across the codebase.
Description check ✅ Passed The description covers all required template sections with sufficient detail: checklist completed, testing steps provided, changelog describing changes, and a screenshot included.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/tri-6733-reset-an-idempotencykey

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
packages/core/src/v3/idempotencyKeys.ts (1)

1-1: Core helper for resetIdempotencyKey looks correct

The new helper cleanly delegates to apiClientManager.clientOrThrow().resetIdempotencyKey(...) and returns the correctly typed { id: string } response. This matches how other v3 helpers wrap the API client.

If you anticipate advanced usage, you could later add an optional requestOptions?: ZodFetchOptions parameter here to pass through to the client, but it’s fine to keep it simple for now.

Also applies to: 136-145

apps/webapp/app/v3/services/resetIdempotencyKey.server.ts (1)

1-44: Avoid redundant read before update in ResetIdempotencyKeyService

The service behavior is correct, but it does a findMany just to check runs.length === 0 and then an updateMany with the same where clause. You can save a round trip by using only updateMany and checking the affected row count:

-    const runs = await this._prisma.taskRun.findMany({
-      where: {
-        idempotencyKey,
-        taskIdentifier,
-        runtimeEnvironmentId: authenticatedEnv.id,
-      },
-      select: {
-        id: true,
-      },
-    });
-
-    if (runs.length === 0) {
-      throw new ServiceValidationError(
-        `No runs found with idempotency key: ${idempotencyKey} and task: ${taskIdentifier}`,
-        404
-      );
-    }
-
-    // Update all runs to clear the idempotency key
-    await this._prisma.taskRun.updateMany({
-      where: {
-        idempotencyKey,
-        taskIdentifier,
-        runtimeEnvironmentId: authenticatedEnv.id,
-      },
-      data: {
-        idempotencyKey: null,
-        idempotencyKeyExpiresAt: null,
-      },
-    });
+    const { count } = await this._prisma.taskRun.updateMany({
+      where: {
+        idempotencyKey,
+        taskIdentifier,
+        runtimeEnvironmentId: authenticatedEnv.id,
+      },
+      data: {
+        idempotencyKey: null,
+        idempotencyKeyExpiresAt: null,
+      },
+    });
+
+    if (count === 0) {
+      throw new ServiceValidationError(
+        `No runs found with idempotency key: ${idempotencyKey} and task: ${taskIdentifier}`,
+        404
+      );
+    }

This keeps the same behavior with fewer queries.

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx (1)

105-109: Consider using plain json() instead of jsonWithSuccessMessage().

The UI component already handles success feedback by checking resetFetcher.data.success === true and displaying a custom toast. Using jsonWithSuccessMessage() sets an additional session-based message that might result in duplicate notifications if there's a global toast handler reading session messages.

-    return jsonWithSuccessMessage(
-      { success: true },
-      request,
-      "Idempotency key reset successfully"
-    );
+    return json({ success: true });

If you want to keep the session message for consistency with other actions, ensure there's no global handler that would display it alongside the custom toast.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d287078 and 0210604.

📒 Files selected for processing (10)
  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts (1 hunks)
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx (1 hunks)
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx (7 hunks)
  • apps/webapp/app/utils/pathBuilder.ts (1 hunks)
  • apps/webapp/app/v3/services/resetIdempotencyKey.server.ts (1 hunks)
  • docs/idempotency.mdx (1 hunks)
  • packages/core/src/v3/apiClient/index.ts (2 hunks)
  • packages/core/src/v3/idempotencyKeys.ts (2 hunks)
  • packages/core/src/v3/schemas/api.ts (1 hunks)
  • packages/trigger-sdk/src/v3/idempotencyKeys.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

Files:

  • apps/webapp/app/v3/services/resetIdempotencyKey.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/idempotencyKeys.ts
  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • packages/core/src/v3/apiClient/index.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/v3/services/resetIdempotencyKey.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • packages/core/src/v3/apiClient/index.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Files:

  • apps/webapp/app/v3/services/resetIdempotencyKey.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/idempotencyKeys.ts
  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • packages/core/src/v3/apiClient/index.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/v3/services/resetIdempotencyKey.server.ts
  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Files:

  • apps/webapp/app/v3/services/resetIdempotencyKey.server.ts
  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/webapp/app/v3/services/**/*.server.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Organize services in the webapp following the pattern app/v3/services/*/*.server.ts

Files:

  • apps/webapp/app/v3/services/resetIdempotencyKey.server.ts
**/*.{js,ts,jsx,tsx,json,md,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier

Files:

  • apps/webapp/app/v3/services/resetIdempotencyKey.server.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/idempotencyKeys.ts
  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • packages/core/src/v3/apiClient/index.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
packages/trigger-sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Files:

  • packages/trigger-sdk/src/v3/idempotencyKeys.ts
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions

Applied to files:

  • apps/webapp/app/v3/services/resetIdempotencyKey.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • docs/idempotency.mdx
  • packages/trigger-sdk/src/v3/idempotencyKeys.ts
  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • packages/core/src/v3/apiClient/index.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution

Applied to files:

  • packages/core/src/v3/idempotencyKeys.ts
  • docs/idempotency.mdx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter

Applied to files:

  • packages/core/src/v3/idempotencyKeys.ts
  • docs/idempotency.mdx
  • packages/trigger-sdk/src/v3/idempotencyKeys.ts
  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts
  • apps/webapp/app/utils/pathBuilder.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run

Applied to files:

  • docs/idempotency.mdx
  • packages/trigger-sdk/src/v3/idempotencyKeys.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties

Applied to files:

  • docs/idempotency.mdx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • docs/idempotency.mdx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • docs/idempotency.mdx
  • packages/trigger-sdk/src/v3/idempotencyKeys.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.batchTrigger()` to trigger multiple runs of a single task with different payloads

Applied to files:

  • docs/idempotency.mdx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.trigger()` with type-only imports to trigger tasks from backend code without importing the task implementation

Applied to files:

  • docs/idempotency.mdx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.batchTrigger()` to trigger multiple runs of a task from inside another task

Applied to files:

  • docs/idempotency.mdx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
📚 Learning: 2025-12-08T15:19:56.801Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.801Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
🧬 Code graph analysis (5)
packages/trigger-sdk/src/v3/idempotencyKeys.ts (2)
packages/core/src/v3/idempotencyKeys.ts (2)
  • createIdempotencyKey (89-98)
  • resetIdempotencyKey (138-145)
packages/core/src/v3/apiClient/index.ts (1)
  • resetIdempotencyKey (452-467)
apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts (2)
apps/webapp/app/v3/services/resetIdempotencyKey.server.ts (1)
  • ResetIdempotencyKeyService (4-44)
packages/core/src/v3/apps/http.ts (1)
  • json (65-75)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx (5)
apps/webapp/app/services/session.server.ts (1)
  • requireUserId (25-35)
apps/webapp/app/utils/pathBuilder.ts (1)
  • v3RunParamsSchema (35-37)
packages/core/src/v3/apps/http.ts (1)
  • json (65-75)
apps/webapp/app/models/message.server.ts (2)
  • jsonWithErrorMessage (125-142)
  • jsonWithSuccessMessage (106-123)
apps/webapp/app/v3/services/resetIdempotencyKey.server.ts (1)
  • ResetIdempotencyKeyService (4-44)
packages/core/src/v3/apiClient/index.ts (2)
packages/core/src/v3/apiClient/core.ts (2)
  • ZodFetchOptions (31-39)
  • zodfetch (71-78)
packages/core/src/v3/schemas/api.ts (2)
  • ResetIdempotencyKeyResponse (721-723)
  • ResetIdempotencyKeyResponse (725-725)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx (2)
apps/webapp/app/components/primitives/Toast.tsx (1)
  • ToastUI (46-94)
apps/webapp/app/utils/pathBuilder.ts (1)
  • v3RunIdempotencyKeyResetPath (332-341)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
🔇 Additional comments (7)
packages/trigger-sdk/src/v3/idempotencyKeys.ts (1)

1-6: SDK surface correctly exposes core resetIdempotencyKey

The idempotencyKeys map now forwards reset directly to the core implementation, which is exactly what you want for SDK ergonomics. No issues here.

apps/webapp/app/utils/pathBuilder.ts (1)

332-341: Reset path helper is consistent with existing routing

v3RunIdempotencyKeyResetPath composes the resource URL using the same helpers and conventions as other v3 paths; the shape matches the new resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset route. Looks good.

packages/core/src/v3/schemas/api.ts (1)

721-725: ResetIdempotencyKeyResponse schema is consistent and sufficient

The new ResetIdempotencyKeyResponse schema and inferred type mirror the other simple { id: string } responses and align with what the service returns. No changes needed.

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx (2)

301-322: LGTM on the toast handling logic.

The fetcher pattern and useEffect dependency array are correct. The success check properly waits for idle state before showing the toast.


573-603: LGTM on the reset UI implementation.

The form correctly posts to the reset path with the task identifier, and the button properly reflects the submitting state. The layout adjustments appropriately accommodate the new reset functionality.

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx (2)

13-15: LGTM on schema definition.

Using zod for validation aligns with coding guidelines for packages/core and apps/webapp.


32-56: LGTM on authorization check.

The query correctly validates that the user is a member of the organization owning this run, providing proper access control.

Comment on lines +1 to +39
import { json } from "@remix-run/server-runtime";
import { z } from "zod";
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
import { ResetIdempotencyKeyService } from "~/v3/services/resetIdempotencyKey.server";

const ParamsSchema = z.object({
key: z.string(),
});

const BodySchema = z.object({
taskIdentifier: z.string().min(1, "Task identifier is required"),
});

export const { action } = createActionApiRoute(
{
params: ParamsSchema,
body: BodySchema,
allowJWT: true,
corsStrategy: "all",
authorization: {
action: "write",
resource: () => ({}),
superScopes: ["write:runs", "admin"],
},
},
async ({ params, body, authentication }) => {
const service = new ResetIdempotencyKeyService();

try {
const result = await service.call(params.key, body.taskIdentifier, authentication.environment);
return json(result, { status: 200 });
} catch (error) {
if (error instanceof Error) {
return json({ error: error.message }, { status: 404 });
}
return json({ error: "Internal Server Error" }, { status: 500 });
}
}
);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Tighten error handling: don’t turn all errors into 404s or leak messages

The happy path is good, but the current try/catch:

} catch (error) {
  if (error instanceof Error) {
    return json({ error: error.message }, { status: 404 });
  }
  return json({ error: "Internal Server Error" }, { status: 500 });
}

has a few issues:

  • Any runtime error (including unexpected server/DB failures) is surfaced as a 404, which is misleading.
  • Raw error.message is returned to clients, potentially leaking internal details.
  • It ignores the status already carried by ServiceValidationError.

A safer pattern is to only treat ServiceValidationError as a 4xx and default everything else to 500 with a generic message. For example:

-import { json } from "@remix-run/server-runtime";
+import { json } from "@remix-run/server-runtime";
+import { ServiceValidationError } from "~/v3/services/baseService.server";
@@
-  async ({ params, body, authentication }) => {
+  async ({ params, body, authentication }) => {
@@
-    try {
-      const result = await service.call(params.key, body.taskIdentifier, authentication.environment);
-      return json(result, { status: 200 });
-    } catch (error) {
-      if (error instanceof Error) {
-        return json({ error: error.message }, { status: 404 });
-      }
-      return json({ error: "Internal Server Error" }, { status: 500 });
-    }
+    try {
+      const result = await service.call(
+        params.key,
+        body.taskIdentifier,
+        authentication.environment
+      );
+      return json(result, { status: 200 });
+    } catch (error) {
+      if (error instanceof ServiceValidationError) {
+        return json({ error: error.message }, { status: error.status ?? 400 });
+      }
+
+      // Optionally log `error` here
+      return json({ error: "Internal Server Error" }, { status: 500 });
+    }
   }
 );

(or, if createActionApiRoute already handles ServiceValidationError globally, you can simply drop the try/catch and let it bubble).

This keeps client semantics accurate and avoids over‑exposing internal error messages.

🤖 Prompt for AI Agents
In apps/webapp/app/routes/api.v1.idempotencyKeys.$key.reset.ts lines 1-39, the
catch block currently maps all Errors to a 404 and returns raw error.message;
update error handling to only treat ServiceValidationError as a client error
(use its status and safe message), and map every other error to a 500 with a
generic "Internal Server Error" payload (do not return raw error.message);
alternatively, if createActionApiRoute already handles ServiceValidationError
globally, remove the try/catch entirely and let errors bubble to the global
handler.

Comment on lines +10 to +11
import { authenticateApiRequest } from "~/services/apiAuth.server";
import { environment } from "effect/Differ";
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove unused imports.

authenticateApiRequest and environment from "effect/Differ" are imported but never used. Additionally, the environment import from "effect/Differ" shadows the local variable environment declared at line 76, which could cause confusion during future maintenance.

 import { ResetIdempotencyKeyService } from "~/v3/services/resetIdempotencyKey.server";
 import { v3RunParamsSchema } from "~/utils/pathBuilder";
-import { authenticateApiRequest } from "~/services/apiAuth.server";
-import { environment } from "effect/Differ";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { authenticateApiRequest } from "~/services/apiAuth.server";
import { environment } from "effect/Differ";
import { ResetIdempotencyKeyService } from "~/v3/services/resetIdempotencyKey.server";
import { v3RunParamsSchema } from "~/utils/pathBuilder";
🤖 Prompt for AI Agents
In
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
around lines 10 to 11, remove the unused imports: delete the import of
authenticateApiRequest and the import of environment from "effect/Differ" (the
latter also shadows the local environment variable declared later). Update the
import list to only include actually used symbols and run the linter or
TypeScript check to verify no remaining references to those names.

Comment on lines +156 to +177
## Resetting idempotency keys

You can reset an idempotency key to clear it from all associated runs. This is useful if you need to allow a task to be triggered again with the same idempotency key.

When you reset an idempotency key, it will be cleared for all runs that match both the task identifier and the idempotency key in the current environment. This allows you to trigger the task again with the same key.

```ts
import { idempotencyKeys } from "@trigger.dev/sdk";

// Reset an idempotency key for a specific task
await idempotencyKeys.reset("my-task", "my-idempotency-key");
```

The `reset` function requires both parameters:
- `taskIdentifier`: The identifier of the task (e.g., `"my-task"`)
- `idempotencyKey`: The idempotency key to reset

After resetting, any subsequent triggers with the same idempotency key will create new task runs instead of returning the existing ones.

<Note>
Resetting an idempotency key only affects runs in the current environment. The reset is scoped to the specific task identifier and idempotency key combination.
</Note>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify which idempotencyKey value to reset and mention not‑found behavior

The new section is clear on scope (task identifier + environment), but the example:

await idempotencyKeys.reset("my-task", "my-idempotency-key");

can be read as passing the original key material rather than the stored key value (typically the value returned from idempotencyKeys.create). The service actually matches on the exact idempotencyKey string stored on the runs, and returns a 404 when no matching runs exist.

I’d recommend tweaking the example to show reusing the same value you originally passed to trigger (e.g. a variable obtained from idempotencyKeys.create) and optionally noting that resetting a non‑existent key/task combination results in a 404, so SDK users know to handle that case.

🤖 Prompt for AI Agents
In docs/idempotency.mdx around lines 156 to 177, clarify that the reset call
must pass the exact stored idempotency key string (typically the value returned
from idempotencyKeys.create or the same variable you passed to trigger) rather
than inferred/original key material, and update the example to show using that
variable; also add a short note that attempting to reset a non-existent task+key
combination returns a 404 so callers should handle that error case.

QueueTypeName,
ReplayRunResponse,
RescheduleRunRequestBody,
ResetIdempotencyKeyResponse,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for both path variants to understand which is actually used
rg -n "idempotency-keys" --type ts --type tsx .
rg -n "idempotencyKeys" --type ts --type tsx .

Repository: triggerdotdev/trigger.dev

Length of output: 124


🏁 Script executed:

# Search for both path variants using pattern matching
rg "idempotency-keys" -g "*.ts" -g "*.tsx"
rg "idempotencyKeys" -g "*.ts" -g "*.tsx"

Repository: triggerdotdev/trigger.dev

Length of output: 3003


🏁 Script executed:

# Search for route files related to idempotency keys reset
fd "idempotency|reset" apps/webapp/app/routes --type f
fd "idempotency|reset" apps/api --type f 2>/dev/null || true

# Read the client method implementation at lines 452-467
sed -n '450,470p' packages/core/src/v3/apiClient/index.ts

Repository: triggerdotdev/trigger.dev

Length of output: 942


🏁 Script executed:

# Search for any Express route registration or middleware that might handle the hyphenated path
rg "idempotency-keys" -g "*.ts" -g "*.tsx" -A 3 -B 3 apps/webapp/app

Repository: triggerdotdev/trigger.dev

Length of output: 51


Fix API path to match server route naming

The Remix route file api.v1.idempotencyKeys.$key.reset.ts maps to /api/v1/idempotencyKeys/:key/reset (camelCase), but the client method uses /api/v1/idempotency-keys/... (hyphenated), causing 404 errors.

Update the client method to use the correct camelCase path:

-      `${this.baseUrl}/api/v1/idempotency-keys/${encodeURIComponent(idempotencyKey)}/reset`,
+      `${this.baseUrl}/api/v1/idempotencyKeys/${encodeURIComponent(idempotencyKey)}/reset`,

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/core/src/v3/apiClient/index.ts around line 32, the client is calling
the wrong hyphenated route (/api/v1/idempotency-keys/...) while the server route
is camelCase (/api/v1/idempotencyKeys/:key/reset); update the client method to
call /api/v1/idempotencyKeys/{key}/reset (use the same camelCase path and
include the key as the path parameter), ensure any URL building/encoding uses
that exact segment, and run/update relevant tests to stop the 404s.

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.

2 participants