Skip to content

fix(core): cap idempotencyKey length at the API boundary#3560

Open
d-cs wants to merge 2 commits into
mainfrom
idempotency-key-length
Open

fix(core): cap idempotencyKey length at the API boundary#3560
d-cs wants to merge 2 commits into
mainfrom
idempotency-key-length

Conversation

@d-cs
Copy link
Copy Markdown
Collaborator

@d-cs d-cs commented May 12, 2026

tasks.trigger, tasks.batchTrigger, batch.create, wait.createToken, wait.forDuration, and the input/session stream waitpoint endpoints all accept a caller-supplied idempotencyKey and store it verbatim against a composite-unique index on TaskRun, BatchTaskRun, or Waitpoint. The schemas had no length cap, so a sufficiently long high-entropy key produced an index row larger than the underlying storage layer can hold. The insert failed at the database, and the caller saw a generic 500 from RunEngineTriggerTaskService.call() / CreateBatchService / waitpoint creation, depending on the endpoint.

Keys produced by idempotencyKeys.create() are 64-character SHA-256 hashes and never trip this — it only manifests for direct REST callers (or SDK callers passing a raw string they generated themselves). Low-entropy keys also sail through, because the storage layer compresses repeated bytes before they reach the index, which is why the failure mode is intermittent and tied to caller-side key shape.

Fix

Add .max(2048, "<field> must be 2048 characters or less") to the seven schemas that feed an indexed idempotencyKey column:

  • TriggerTaskRequestBody.options.idempotencyKey
  • BatchTriggerTaskItem.options.idempotencyKey
  • CreateBatchRequestBody.idempotencyKey
  • CreateWaitpointTokenRequestBody.idempotencyKey
  • CreateInputStreamWaitpointRequestBody.idempotencyKey
  • CreateSessionStreamWaitpointRequestBody.idempotencyKey
  • WaitForDurationRequestBody.idempotencyKey

Plus the idempotency-key HTTP header on the trigger route (and the three batch routes that re-export HeadersSchema). The header schema is lifted out of api.v1.tasks.$taskId.trigger.ts into apps/webapp/app/v3/triggerHeaders.server.ts so it can be exercised in tests without dragging the route's import-time side effects.

The 2048 character ceiling is chosen to sit safely under the per-row index limit while staying generous against existing callers — keys that fit before still fit. Oversized keys now return a structured Zod 400 instead of a generic 500.

Limit is documented under Idempotency key in docs/limits.mdx and as a <Note> on docs/idempotency.mdx.

Test plan

  • 15 schema unit tests added (packages/core/src/v3/schemas/idempotencyKey.test.ts, apps/webapp/test/routes/triggerHeaders.test.ts) — rejection-with-message + boundary acceptance for each capped schema. The webapp test exercises the extracted TriggerHeadersSchema directly with no mocks.
  • pnpm run build --filter @trigger.dev/core
  • pnpm run typecheck --filter webapp
  • End-to-end verified locally: baseline (small key) → 200; 3000-char high-entropy header → 400 with the expected Zod error; same key at the 2048 boundary → 200; same key with the cap reverted → the database rejected the insert and the route returned 500 to the caller. Cap restored.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 12, 2026

🦋 Changeset detected

Latest commit: bc0d7f5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 29 packages
Name Type
@trigger.dev/core Patch
@trigger.dev/build Patch
trigger.dev Patch
@trigger.dev/python Patch
@trigger.dev/redis-worker Patch
@trigger.dev/schema-to-json Patch
@trigger.dev/sdk Patch
@internal/cache Patch
@internal/clickhouse Patch
@internal/llm-model-catalog Patch
@internal/redis Patch
@internal/replication Patch
@internal/run-engine Patch
@internal/schedule-engine Patch
@internal/testcontainers Patch
@internal/tracing Patch
@internal/tsql Patch
@internal/zod-worker Patch
d3-chat Patch
references-d3-openai-agents Patch
references-nextjs-realtime Patch
references-realtime-hooks-test Patch
references-realtime-streams Patch
references-telemetry Patch
@internal/sdk-compat-tests Patch
@trigger.dev/react-hooks Patch
@trigger.dev/rsc Patch
@trigger.dev/database Patch
@trigger.dev/otlp-importer Patch

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 39aac3a7-d669-4fcf-971e-833d0593cf8d

📥 Commits

Reviewing files that changed from the base of the PR and between 6364065 and cc77711.

📒 Files selected for processing (5)
  • .changeset/cap-idempotency-key-length.md
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • docs/idempotency.mdx
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
✅ Files skipped from review due to trivial changes (3)
  • .changeset/cap-idempotency-key-length.md
  • docs/idempotency.mdx
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/v3/schemas/api.ts
📜 Recent review details
⏰ 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). (29)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 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 (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 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 (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: units / e2e-webapp / 🧪 E2E Tests: Webapp
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{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/routes/api.v1.tasks.$taskId.trigger.ts
{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/routes/api.v1.tasks.$taskId.trigger.ts
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

Files:

  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
apps/webapp/**/*.{ts,tsx}

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

apps/webapp/**/*.{ts,tsx}: Access environment variables through the env export of env.server.ts instead of directly accessing process.env
Use subpath exports from @trigger.dev/core package instead of importing from the root @trigger.dev/core path

Use named constants for sentinel/placeholder values (e.g. const UNSET_VALUE = '__unset__') instead of raw string literals scattered across comparisons

Files:

  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
{apps,internal-packages}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use pnpm run typecheck to verify changes in apps and internal packages (apps/*, internal-packages/*) instead of build, which proves almost nothing about correctness

Files:

  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
{package.json,**/*.{ts,tsx,js}}

📄 CodeRabbit inference engine (CLAUDE.md)

Pin Zod to version 3.25.76 exactly across the entire monorepo - never use a different version or version range

Files:

  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js}: Import from @trigger.dev/core using subpaths only, never the root export
Always import tasks from @trigger.dev/sdk, never from @trigger.dev/sdk/v3 or deprecated client.defineJob
Add crumbs to code using // @Crumbs comments or `// `#region` `@crumbs blocks for debug tracing during development

Files:

  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
**/*.{ts,tsx,js,jsx,json,md,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Code formatting is enforced using Prettier. Run pnpm run format before committing

Files:

  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
🧠 Learnings (2)
📚 Learning: 2026-03-22T13:26:12.060Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/code/TextEditor.tsx:81-86
Timestamp: 2026-03-22T13:26:12.060Z
Learning: In the triggerdotdev/trigger.dev codebase, do not flag `navigator.clipboard.writeText(...)` calls for `missing-await`/`unhandled-promise` issues. These clipboard writes are intentionally invoked without `await` and without `catch` handlers across the project; keep that behavior consistent when reviewing TypeScript/TSX files (e.g., usages like in `apps/webapp/app/components/code/TextEditor.tsx`).

Applied to files:

  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
📚 Learning: 2026-03-22T19:24:14.403Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3187
File: apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts:200-204
Timestamp: 2026-03-22T19:24:14.403Z
Learning: In the triggerdotdev/trigger.dev codebase, webhook URLs are not expected to contain embedded credentials/secrets (e.g., fields like `ProjectAlertWebhookProperties` should only hold credential-free webhook endpoints). During code review, if you see logging or inclusion of raw webhook URLs in error messages, do not automatically treat it as a credential-leak/secrets-in-logs issue by default—first verify the URL does not contain embedded credentials (for example, no username/password in the URL, no obvious secret/token query params or fragments). If the URL is credential-free per this project’s conventions, allow the logging.

Applied to files:

  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
🔇 Additional comments (1)
apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts (1)

31-34: LGTM!


Walkthrough

This PR enforces a 2048-character maximum length on idempotency keys across the Trigger.dev API. Changes include: adding .max(2048) validation constraints to seven request-body schemas in the core package (TriggerTaskRequestBody, BatchTriggerTaskItem, CreateBatchRequestBody, and four wait-related schemas); updating the webapp header validation for the idempotency-key HTTP header to enforce the same limit; adding comprehensive Vitest coverage across all affected schemas; and documenting the limit in the idempotency guide with a note that SDK-generated keys (64 characters) are unaffected.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding a 2048-character length cap to idempotencyKey at the API validation boundary to prevent database failures.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering the problem statement, the fix details, rationale, documentation updates, and extensive test plan verification with local E2E confirmation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch idempotency-key-length

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@d-cs d-cs force-pushed the idempotency-key-length branch 2 times, most recently from e7fe389 to d28de1e Compare May 12, 2026 12:41
Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
apps/webapp/app/v3/triggerHeaders.server.ts (1)

18-19: ⚡ Quick win

Consider using .nullish() for consistency with other optional headers.

The traceparent and tracestate fields use .optional(), while all other optional headers in this schema use .nullish(). For consistency and defensive coding, consider using .nullish() here as well to match the pattern established by the rest of the schema.

♻️ Unified nullish pattern
-  traceparent: z.string().optional(),
-  tracestate: z.string().optional(),
+  traceparent: z.string().nullish(),
+  tracestate: z.string().nullish(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/webapp/app/v3/triggerHeaders.server.ts` around lines 18 - 19, The schema
in triggerHeaders.server.ts uses z.string().optional() for the traceparent and
tracestate fields while other optional headers use .nullish(); update the
traceparent and tracestate definitions to use .nullish() instead of .optional()
so they follow the unified nullish pattern (look for the traceparent and
tracestate symbols inside the trigger headers schema), and run/type-check tests
to ensure no downstream type changes break usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/webapp/app/v3/triggerHeaders.server.ts`:
- Around line 18-19: The schema in triggerHeaders.server.ts uses
z.string().optional() for the traceparent and tracestate fields while other
optional headers use .nullish(); update the traceparent and tracestate
definitions to use .nullish() instead of .optional() so they follow the unified
nullish pattern (look for the traceparent and tracestate symbols inside the
trigger headers schema), and run/type-check tests to ensure no downstream type
changes break usage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1652cb7e-59c6-4762-adff-2e4b2d1b9e68

📥 Commits

Reviewing files that changed from the base of the PR and between 41a486e and e7fe389.

📒 Files selected for processing (8)
  • .changeset/cap-idempotency-key-length.md
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/test/routes/triggerHeaders.test.ts
  • docs/idempotency.mdx
  • docs/limits.mdx
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
📜 Review details
⏰ 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). (27)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: units / e2e-webapp / 🧪 E2E Tests: Webapp
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: typecheck / typecheck
🧰 Additional context used
📓 Path-based instructions (18)
docs/**/*.mdx

📄 CodeRabbit inference engine (docs/CLAUDE.md)

docs/**/*.mdx: MDX documentation pages must include frontmatter with title (required), description (required), and sidebarTitle (optional) in YAML format
Use Mintlify components for structured content: , , , , , , /, /
Always import from @trigger.dev/sdk in code examples (never from @trigger.dev/sdk/v3)
Code examples must be complete and runnable where possible
Use language tags in code fences: typescript, bash, json

Files:

  • docs/limits.mdx
  • docs/idempotency.mdx
**/*.{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/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
{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/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
**/*.{test,spec}.{ts,tsx}

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

Use vitest for all tests in the Trigger.dev repository

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
apps/webapp/**/*.{ts,tsx}

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

apps/webapp/**/*.{ts,tsx}: Access environment variables through the env export of env.server.ts instead of directly accessing process.env
Use subpath exports from @trigger.dev/core package instead of importing from the root @trigger.dev/core path

Use named constants for sentinel/placeholder values (e.g. const UNSET_VALUE = '__unset__') instead of raw string literals scattered across comparisons

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
apps/webapp/**/*.test.{ts,tsx}

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

Do not import env.server.ts directly or indirectly into test files; instead pass environment-dependent values through options/parameters to make code testable

For testable code, never import env.server.ts in test files. Pass configuration as options instead (e.g., realtimeClient.server.ts takes config as constructor arg, realtimeClientGlobal.server.ts creates singleton with env config)

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
{apps,internal-packages}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use pnpm run typecheck to verify changes in apps and internal packages (apps/*, internal-packages/*) instead of build, which proves almost nothing about correctness

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
**/*.test.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx,js}: Use vitest exclusively for testing and never mock anything - use testcontainers instead
Place test files next to source files using the pattern MyService.ts -> MyService.test.ts

**/*.test.{ts,tsx,js}: Use vitest for unit testing and run tests with pnpm run test
Test files should live beside the files under test with descriptive describe and it blocks
Tests should avoid mocks or stubs and use helpers from @internal/testcontainers when Redis or Postgres are needed

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use testcontainers with redisTest, postgresTest, or containerTest from @internal/testcontainers for testing with Redis/PostgreSQL dependencies

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
{package.json,**/*.{ts,tsx,js}}

📄 CodeRabbit inference engine (CLAUDE.md)

Pin Zod to version 3.25.76 exactly across the entire monorepo - never use a different version or version range

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js}: Import from @trigger.dev/core using subpaths only, never the root export
Always import tasks from @trigger.dev/sdk, never from @trigger.dev/sdk/v3 or deprecated client.defineJob
Add crumbs to code using // @Crumbs comments or `// `#region` `@crumbs blocks for debug tracing during development

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
**/*.{ts,tsx,js,jsx,json,md,css,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Code formatting is enforced using Prettier. Run pnpm run format before committing

Files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
apps/webapp/**/*.server.ts

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

apps/webapp/**/*.server.ts: Never use request.signal for detecting client disconnects. Use getRequestAbortSignal() from app/services/httpAsyncStorage.server.ts instead, which is wired directly to Express res.on('close') and fires reliably
Access environment variables via env export from app/env.server.ts. Never use process.env directly
Always use findFirst instead of findUnique in Prisma queries. findUnique has an implicit DataLoader that batches concurrent calls and has active bugs even in Prisma 6.x (uppercase UUIDs returning null, composite key SQL correctness issues, 5-10x worse performance). findFirst is never batched and avoids this entire class of issues

Files:

  • apps/webapp/app/v3/triggerHeaders.server.ts
packages/core/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/core/CLAUDE.md)

Never import the root package (@trigger.dev/core). Always use subpath imports such as @trigger.dev/core/v3, @trigger.dev/core/v3/utils, @trigger.dev/core/logger, or @trigger.dev/core/schemas

Files:

  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
packages/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use pnpm run build to verify changes in public packages (packages/*)

Files:

  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
{packages,integrations}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

When modifying any public package (packages/* or integrations/*), add a changeset using pnpm run changeset:add

Files:

  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
🧠 Learnings (7)
📚 Learning: 2026-03-10T12:44:14.176Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3200
File: docs/config/config-file.mdx:353-368
Timestamp: 2026-03-10T12:44:14.176Z
Learning: In the trigger.dev repo, docs PRs are often companions to implementation PRs. When reviewing docs PRs (MDX files under docs/), check the PR description for any companion/related PR references and verify that the documented features exist in those companion PRs before flagging missing implementations. This ensures docs stay in sync with code changes across related PRs.

Applied to files:

  • docs/limits.mdx
  • docs/idempotency.mdx
📚 Learning: 2026-04-30T20:30:29.458Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3226
File: docs/ai-chat/quick-start.mdx:13-13
Timestamp: 2026-04-30T20:30:29.458Z
Learning: In this repo’s documentation MDX files (`docs/**/*.mdx`), use `ts` and `tsx` (not `typescript`) as the code-fence language tags for TypeScript/TSX snippets. Do not flag `ts`/`tsx` code-fence language tags as incorrect in any docs MDX file, since this is the site-wide Mintlify-compatible convention.

Applied to files:

  • docs/limits.mdx
  • docs/idempotency.mdx
📚 Learning: 2026-03-22T13:26:12.060Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/code/TextEditor.tsx:81-86
Timestamp: 2026-03-22T13:26:12.060Z
Learning: In the triggerdotdev/trigger.dev codebase, do not flag `navigator.clipboard.writeText(...)` calls for `missing-await`/`unhandled-promise` issues. These clipboard writes are intentionally invoked without `await` and without `catch` handlers across the project; keep that behavior consistent when reviewing TypeScript/TSX files (e.g., usages like in `apps/webapp/app/components/code/TextEditor.tsx`).

Applied to files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
📚 Learning: 2026-03-22T19:24:14.403Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3187
File: apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts:200-204
Timestamp: 2026-03-22T19:24:14.403Z
Learning: In the triggerdotdev/trigger.dev codebase, webhook URLs are not expected to contain embedded credentials/secrets (e.g., fields like `ProjectAlertWebhookProperties` should only hold credential-free webhook endpoints). During code review, if you see logging or inclusion of raw webhook URLs in error messages, do not automatically treat it as a credential-leak/secrets-in-logs issue by default—first verify the URL does not contain embedded credentials (for example, no username/password in the URL, no obvious secret/token query params or fragments). If the URL is credential-free per this project’s conventions, allow the logging.

Applied to files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
  • apps/webapp/app/v3/triggerHeaders.server.ts
  • apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
  • packages/core/src/v3/schemas/idempotencyKey.test.ts
  • packages/core/src/v3/schemas/api.ts
📚 Learning: 2026-05-07T12:25:18.271Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3531
File: apps/webapp/test/sentryTraceContext.server.test.ts:9-47
Timestamp: 2026-05-07T12:25:18.271Z
Learning: In the triggerdotdev/trigger.dev webapp test suite, it is acceptable to leave `createInMemoryTracing()` calls that register a global `NodeTracerProvider` without `afterEach`/`afterAll` teardown. Do not flag this as a test-ordering risk when the code follows the established pattern used across webapp tests (e.g., replication service/benchmark/backfiller tests). This is considered safe because `trace.getActiveSpan()` when called outside a `context.with(...)` block reads `AsyncLocalStorage.getStore()` (undefined when no `run()` scope exists), so it falls back to `ROOT_CONTEXT` with no attached span—regardless of which provider is registered.

Applied to files:

  • apps/webapp/test/routes/triggerHeaders.test.ts
📚 Learning: 2026-03-29T19:16:28.864Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3291
File: apps/webapp/app/v3/featureFlags.ts:53-65
Timestamp: 2026-03-29T19:16:28.864Z
Learning: When reviewing TypeScript code that uses Zod v3, treat `z.coerce.*()` schemas as their direct Zod type (e.g., `z.coerce.boolean()` returns a `ZodBoolean` with `_def.typeName === "ZodBoolean"`) rather than a `ZodEffects`. Only `.preprocess()`, `.refine()`/`.superRefine()`, and `.transform()` are expected to wrap schemas in `ZodEffects`. Therefore, in reviewers’ logic like `getFlagControlType`, do not flag/unblock failures that require unwrapping `ZodEffects` when the input schema is a `z.coerce.*` schema.

Applied to files:

  • apps/webapp/app/v3/triggerHeaders.server.ts
📚 Learning: 2026-05-05T09:38:02.512Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3523
File: apps/webapp/app/routes/api.v3.batches.ts:178-181
Timestamp: 2026-05-05T09:38:02.512Z
Learning: When reviewing code that catches `ServiceValidationError` in `*.server.ts` files, do not blindly forward `error.status` to HTTP responses, because SVEs may be thrown with non-default statuses (e.g., 400/500) and forwarding them can cause client-visible behavioral regressions (e.g., surfacing 500s to clients). Prefer a safe default response status of `error.status ?? 422`, but only after confirming via the reachable call graph that the caught `ServiceValidationError` instances are expected to carry those non-default statuses; otherwise, normalize to `422` to avoid unexpected client-visible 5xx behavior.

Applied to files:

  • apps/webapp/app/v3/triggerHeaders.server.ts
🔇 Additional comments (13)
apps/webapp/test/routes/triggerHeaders.test.ts (1)

1-30: LGTM!

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

200-206: LGTM!


258-264: LGTM!


373-379: LGTM!


1371-1377: LGTM!


1416-1422: LGTM!


1455-1461: LGTM!


1750-1756: LGTM!

packages/core/src/v3/schemas/idempotencyKey.test.ts (1)

1-172: LGTM!

docs/limits.mdx (1)

101-101: LGTM!

docs/idempotency.mdx (1)

111-113: LGTM!

.changeset/cap-idempotency-key-length.md (1)

1-6: LGTM!

apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts (1)

24-31: LGTM!

@d-cs d-cs force-pushed the idempotency-key-length branch from d28de1e to e877410 Compare May 12, 2026 12:46
@mintlify
Copy link
Copy Markdown
Contributor

mintlify Bot commented May 12, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
trigger 🟢 Ready View Preview May 12, 2026, 12:55 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@d-cs
Copy link
Copy Markdown
Collaborator Author

d-cs commented May 12, 2026

Verified — keeping as is.

The .optional() on traceparent and tracestate predates this PR; the original HeadersSchema on main had the same mix (lines 30–43 of apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts before the extraction). I copied the schema verbatim when lifting it into triggerHeaders.server.ts so the test could import it without route side effects, so this is unchanged behavior, not a new inconsistency.

Functionally .optional() and .nullish() are equivalent for these fields — HTTP headers reach the schema as string | undefined, never literal null — so the diff would be cosmetic only. Out of scope for an idempotency-key cap PR; happy to harmonize in a separate cleanup if anyone wants to take it.

Reject overlong idempotencyKey values before they reach the unique
idempotency index on TaskRun, BatchTaskRun, and Waitpoint. Past the
per-row size limit, the database rejected the insert and the caller
saw a generic 500 from RunEngineTriggerTaskService / CreateBatchService
/ waitpoint creation, depending on the endpoint. Inputs are capped at
2048 characters — well above idempotencyKeys.create()'s 64-character
hash and above any realistic raw key.

Applies to tasks.trigger, tasks.batchTrigger, batch.create (Phase 1
streaming batches), wait.createToken, wait.forDuration, and the
input/session stream waitpoint endpoints. Over-limit requests now
return a structured 400 instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@d-cs d-cs force-pushed the idempotency-key-length branch from 6364065 to cc77711 Compare May 12, 2026 13:39
@d-cs d-cs self-assigned this May 12, 2026
@d-cs d-cs marked this pull request as ready for review May 12, 2026 13:41
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 0 potential issues.

View 1 additional finding in Devin Review.

Open in Devin Review

@ericallam
Copy link
Copy Markdown
Member

@d-cs Usually we split out docs updates into their own PRs (cause merging docs changes into main causes the docs site to be updated immediately, and there is usually a decent time between merging a change into main and releasing to users), but in this case its A-OK because the change is pretty minor and fine to go live "early".

Comment thread apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts
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