Skip to content

fix(cli,core): stop dev workers spinning at 100% CPU after parent CLI disconnect#3491

Merged
ericallam merged 1 commit intomainfrom
fix/dev-worker-disconnect-loop
May 1, 2026
Merged

fix(cli,core): stop dev workers spinning at 100% CPU after parent CLI disconnect#3491
ericallam merged 1 commit intomainfrom
fix/dev-worker-disconnect-loop

Conversation

@ericallam
Copy link
Copy Markdown
Member

Orphaned trigger-dev-run-worker processes were pinning CPU at 100% after the dev CLI exited — stuck in an uncaughtException feedback loop where a closed IPC channel kept throwing ERR_IPC_CHANNEL_CLOSED back into a handler that itself called process.send.

Fix:

  • ZodIpcConnection no-ops sends when the channel is disconnected.
  • Dev workers exit on process.disconnect instead of being re-parented to init.
  • All worker uncaughtException handlers route through a safeSend guard so the handler can never re-enter itself.

Verified end-to-end: kill -9 of the dev CLI now cleans up all child workers within ~2s.

… disconnect

Orphaned trigger-dev-run-worker and trigger-dev-index-worker processes were
getting stuck in an uncaughtException feedback loop when the parent CLI closed
the IPC channel: a periodic IPC send via process.send would throw
ERR_IPC_CHANNEL_CLOSED, which re-entered the same handler that itself called
process.send. The loop was amplified by source-map-support's prepareStackTrace
running every iteration.

- @trigger.dev/core: ZodIpcConnection drops packets when process.connected is
  false and swallows synchronous send errors, so closed-channel sends no longer
  throw out of the IPC layer.
- trigger.dev (cli-v3): dev-run-worker and dev-index-worker now exit cleanly via
  process.on("disconnect") instead of being re-parented to init.
- trigger.dev (cli-v3): all four worker entry points wrap their uncaughtException
  process.send calls in safeSend, which checks process.connected and swallows
  synchronous throws so a closed channel can never re-enter the handler.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 1, 2026

🦋 Changeset detected

Latest commit: 67cab6c

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 Patch
@trigger.dev/build 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

@ericallam ericallam marked this pull request as ready for review May 1, 2026 11:33
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 1, 2026

Walkthrough

This pull request fixes a CPU busy-loop issue in dev workers that occurs when the parent CLI disconnects. The fix prevents IPC failures from cascading into recursive error handling by introducing a safeSend helper function across multiple worker entry points that guards process.send calls against connection loss and exceptions. Worker processes now exit cleanly on IPC disconnection, and the ZodIpcConnection class checks connection state before attempting to send packets. The changes include adding process disconnect event listeners, wrapping send operations in try/catch blocks, and checking process.connected before calling process.send.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description lacks required sections from the template (Closes #issue, Checklist, Testing steps, Screenshots), but provides a clear, technical explanation of the problem and solution that meaningfully addresses the PR's purpose. Add the PR template sections including issue reference, checklist confirmation, and testing methodology to meet repository standards.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary fix: preventing dev workers from spinning at 100% CPU after parent CLI disconnect, which aligns perfectly with the changeset and all modified files.
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 fix/dev-worker-disconnect-loop

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/cli-v3/src/entryPoints/dev-index-worker.ts (1)

205-212: ⚠️ Potential issue | 🟠 Major

Use safeSend(msg) instead of process.send?.(msg) on line 210 for consistency and robustness.

The TASKS_FAILED_TO_PARSE error handler path (line 210) directly calls process.send?.(msg) while the INDEX_COMPLETE success path (line 201) correctly uses safeSend(msg). The safeSend wrapper checks process.connected, guards against IPC channel errors, and prevents busy-looping on a dead channel—protections that should apply to all message-sending paths, especially error handlers.

Suggested fix
   if (err instanceof ZodSchemaParsedError) {
     return sendMessageInCatalog(
       indexerToWorkerMessages,
       "TASKS_FAILED_TO_PARSE",
       { zodIssues: err.error.issues, tasks },
       async (msg) => {
-        process.send?.(msg);
+        safeSend(msg);
       }
     );
   } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli-v3/src/entryPoints/dev-index-worker.ts` around lines 205 - 212,
Replace the direct IPC send call in the TASKS_FAILED_TO_PARSE handler with the
existing safeSend wrapper: inside the sendMessageInCatalog call for
"TASKS_FAILED_TO_PARSE" (currently using process.send?.(msg)), call
safeSend(msg) instead so the error path uses the same process.connected and
error-guarding logic as the INDEX_COMPLETE path; locate sendMessageInCatalog
usage for "TASKS_FAILED_TO_PARSE" and swap process.send?.(msg) to safeSend(msg).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/cli-v3/src/entryPoints/dev-index-worker.ts`:
- Around line 205-212: Replace the direct IPC send call in the
TASKS_FAILED_TO_PARSE handler with the existing safeSend wrapper: inside the
sendMessageInCatalog call for "TASKS_FAILED_TO_PARSE" (currently using
process.send?.(msg)), call safeSend(msg) instead so the error path uses the same
process.connected and error-guarding logic as the INDEX_COMPLETE path; locate
sendMessageInCatalog usage for "TASKS_FAILED_TO_PARSE" and swap
process.send?.(msg) to safeSend(msg).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b3ae7520-c3f2-4506-9f16-96530545b072

📥 Commits

Reviewing files that changed from the base of the PR and between 1dfd595 and 67cab6c.

📒 Files selected for processing (6)
  • .changeset/dev-worker-disconnect-loop.md
  • packages/cli-v3/src/entryPoints/dev-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/core/src/v3/zodIpc.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). (28)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 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 (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • 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 (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 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 / Deno Runtime
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: typecheck / typecheck
🧰 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:

  • packages/core/src/v3/zodIpc.ts
  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.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:

  • packages/core/src/v3/zodIpc.ts
**/*.{ts,tsx,js,jsx}

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

Use function declarations instead of default exports

Add crumbs as you write code using // @Crumbs comments or `// `#region` `@crumbs blocks. These are temporary debug instrumentation and must be stripped using agentcrumbs strip before merge.

Files:

  • packages/core/src/v3/zodIpc.ts
  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.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:

  • packages/core/src/v3/zodIpc.ts
  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • packages/core/src/v3/zodIpc.ts
  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.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/zodIpc.ts
**/*.ts{,x}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from @trigger.dev/sdk when writing Trigger.dev tasks. Never use @trigger.dev/sdk/v3 or deprecated client.defineJob.

Files:

  • packages/core/src/v3/zodIpc.ts
  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.ts
packages/cli-v3/src/entryPoints/**/*

📄 CodeRabbit inference engine (packages/cli-v3/CLAUDE.md)

Code in src/entryPoints/ runs inside customer containers and is a different runtime environment from the CLI - changes affect deployed task execution directly

Files:

  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.ts
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/redis-worker/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:43.173Z
Learning: Applies to packages/redis-worker/**/redis-worker/src/worker.ts : Worker loop and job processing should implement concurrency control in src/worker.ts
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/redis-worker/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:43.173Z
Learning: Do NOT add new jobs to zodworker (internal/zodworker) or graphile-worker
📚 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:

  • packages/core/src/v3/zodIpc.ts
  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.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:

  • packages/core/src/v3/zodIpc.ts
  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.ts
📚 Learning: 2026-04-16T14:19:16.330Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: apps/webapp/CLAUDE.md:0-0
Timestamp: 2026-04-16T14:19:16.330Z
Learning: Applies to apps/webapp/app/v3/**Worker.server.ts : Do NOT add new jobs using zodworker/graphile-worker (legacy). Background job workers use `trigger.dev/redis-worker` via files like `app/v3/commonWorker.server.ts`, `app/v3/alertsWorker.server.ts`, `app/v3/batchTriggerWorker.server.ts`

Applied to files:

  • .changeset/dev-worker-disconnect-loop.md
📚 Learning: 2026-03-26T23:24:54.682Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3114
File: apps/supervisor/src/workloadServer/index.ts:817-825
Timestamp: 2026-03-26T23:24:54.682Z
Learning: In `apps/supervisor/src/workloadServer/index.ts` (`WorkloadServer.stop()`), pending items returned by `this.snapshotDelayWheel?.stop()` are intentionally logged and dropped rather than dispatched. The entire supervisor is shutting down, so the snapshot callback URL would point at a dead server; dispatching snapshots during teardown would create orphaned gateway callbacks. Runners detect the supervisor is gone and reconnect to a new supervisor instance, which re-triggers the snapshot workflow. Do not flag the drop-on-shutdown behavior as a bug.

Applied to files:

  • .changeset/dev-worker-disconnect-loop.md
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.ts
📚 Learning: 2026-03-02T12:43:43.173Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/redis-worker/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:43.173Z
Learning: Applies to packages/redis-worker/**/*@(job|queue|worker|background).{ts,tsx} : Use trigger.dev/redis-worker for all new background job implementations, replacing graphile-worker and zodworker

Applied to files:

  • .changeset/dev-worker-disconnect-loop.md
📚 Learning: 2025-10-08T11:48:12.327Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2593
File: packages/core/src/v3/workers/warmStartClient.ts:168-170
Timestamp: 2025-10-08T11:48:12.327Z
Learning: The trigger.dev runners execute only in Node 21 and 22 environments, so modern Node.js APIs like AbortSignal.any (introduced in v20.3.0) are supported.

Applied to files:

  • .changeset/dev-worker-disconnect-loop.md
📚 Learning: 2026-03-02T12:43:34.140Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/cli-v3/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:34.140Z
Learning: Applies to packages/cli-v3/src/build/**/* : Bundle worker code using the build system in `src/build/` based on configuration from `trigger.config.ts`

Applied to files:

  • .changeset/dev-worker-disconnect-loop.md
📚 Learning: 2026-03-25T15:29:25.889Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2026-03-25T15:29:25.889Z
Learning: Run `npx trigger.devlatest dev` to start the Trigger.dev development server

Applied to files:

  • .changeset/dev-worker-disconnect-loop.md
📚 Learning: 2026-04-13T21:44:00.644Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3368
File: apps/webapp/app/services/taskIdentifierRegistry.server.ts:49-58
Timestamp: 2026-04-13T21:44:00.644Z
Learning: In `triggerdotdev/trigger.dev`, a deployment with zero tasks is not a realistic scenario in practice. Do not flag missing handling for empty-task deployments in `apps/webapp/app/services/taskIdentifierRegistry.server.ts` or related registry/sync logic.

Applied to files:

  • .changeset/dev-worker-disconnect-loop.md
📚 Learning: 2026-03-22T13:26:15.187Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/code/TextEditor.tsx:81-86
Timestamp: 2026-03-22T13:26:15.187Z
Learning: In triggerdotdev/trigger.dev, `navigator.clipboard.writeText()` is intentionally called without awaiting or catching errors throughout the codebase (e.g., `apps/webapp/app/components/code/TextEditor.tsx`). Do not raise unhandled-promise or missing-await issues for clipboard write calls in this project.

Applied to files:

  • .changeset/dev-worker-disconnect-loop.md
📚 Learning: 2026-03-02T12:43:43.173Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/redis-worker/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:43.173Z
Learning: Do NOT add new jobs to zodworker (internal/zodworker) or graphile-worker

Applied to files:

  • .changeset/dev-worker-disconnect-loop.md
📚 Learning: 2024-10-18T15:41:52.352Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 1418
File: packages/core/src/v3/errors.ts:364-371
Timestamp: 2024-10-18T15:41:52.352Z
Learning: In `packages/core/src/v3/errors.ts`, within the `taskRunErrorEnhancer` function, `error.message` is always defined, so it's safe to directly call `error.message.includes("SIGTERM")` without additional checks.

Applied to files:

  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
📚 Learning: 2026-03-02T12:43:43.173Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: packages/redis-worker/CLAUDE.md:0-0
Timestamp: 2026-03-02T12:43:43.173Z
Learning: Applies to packages/redis-worker/**/redis-worker/src/worker.ts : Worker loop and job processing should implement concurrency control in src/worker.ts

Applied to files:

  • packages/cli-v3/src/entryPoints/managed-run-worker.ts
  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
  • packages/cli-v3/src/entryPoints/managed-index-worker.ts
  • packages/cli-v3/src/entryPoints/dev-index-worker.ts
📚 Learning: 2024-10-08T15:31:34.807Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 1389
File: apps/coordinator/src/index.ts:403-0
Timestamp: 2024-10-08T15:31:34.807Z
Learning: In `apps/coordinator/src/index.ts`, moving the `exitRun` and `crashRun` functions outside the `onConnection` method is not feasible because of TypeScript type issues with `socket`. Keeping these functions inside `onConnection` is preferred.

Applied to files:

  • packages/cli-v3/src/entryPoints/dev-run-worker.ts
📚 Learning: 2026-04-23T13:26:31.290Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3430
File: apps/webapp/app/presenters/v3/RunStreamPresenter.server.ts:0-0
Timestamp: 2026-04-23T13:26:31.290Z
Learning: In `apps/webapp/app/utils/sse.ts` (and callers such as `apps/webapp/app/presenters/v3/RunStreamPresenter.server.ts`), the string-reason type passed to `.abort()` is NOT load-bearing for heap retention in SSE streams on Node 20. Isolation testing (2000 requests × 200 KB payload) confirmed `.abort()` vs `.abort("string")` produces identical heap deltas once `AbortSignal.any` is removed. The actual root cause of the SSE memory leak was `AbortSignal.any`, which registers signals with a FinalizationRegistry that does not reliably release them on abort (see nodejs/node#54614 and `#55351`). Named sentinel constants (`ABORT_REASON_*`, exported from `sse.ts`) are a readability/style win per the CLAUDE.md named-constant guideline, not a correctness fix. Do not flag `.abort()` vs `.abort("reason string")` as a memory-retention issue in future reviews of this codebase.

Applied to files:

  • packages/cli-v3/src/entryPoints/dev-index-worker.ts
📚 Learning: 2026-03-26T23:23:33.560Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3114
File: apps/supervisor/src/index.ts:454-457
Timestamp: 2026-03-26T23:23:33.560Z
Learning: In `apps/supervisor/src/index.ts` (`ManagedSupervisor.stop()`), the shutdown order is intentional: `workloadServer.stop()` is called first to drain the timer wheel and stop accepting new snapshot callbacks, then `workerSession.stop()` is called to tear down the dequeue loop and runner connections (it handles its own in-flight drain). Reversing the order would leave the workloadServer up with no session to report to after runners disconnect. Do not flag this ordering as incorrect.

Applied to files:

  • packages/cli-v3/src/entryPoints/dev-index-worker.ts
🔇 Additional comments (6)
packages/cli-v3/src/entryPoints/dev-run-worker.ts (1)

80-127: Good IPC disconnect hardening in worker entrypoint.

process.on("disconnect") + safeSend() in the uncaughtException path cleanly addresses the re-entrant send loop risk.

packages/cli-v3/src/entryPoints/managed-index-worker.ts (1)

30-63: safeSend rollout here looks consistent and complete.

The guarded send path is correctly applied in both uncaught-exception reporting and message forwarding callbacks.

Also applies to: 203-213

packages/cli-v3/src/entryPoints/managed-run-worker.ts (1)

80-120: Uncaught-exception IPC reporting is safely guarded now.

Using safeSend() here closes the send-throw re-entry path without changing message shape.

packages/core/src/v3/zodIpc.ts (1)

260-274: #sendPacket guard is correct for disconnected IPC channels.

Early drop + wrapped process.send is the right protection against closed-channel sync throws.

packages/cli-v3/src/entryPoints/dev-index-worker.ts (1)

30-69: Disconnect handling + safeSend integration is solid in the primary paths.

These changes correctly prevent the uncaughtException IPC feedback loop in normal send flows.

Also applies to: 200-202

.changeset/dev-worker-disconnect-loop.md (1)

1-7: Changeset entry is clear and appropriately scoped.

Patch bump + rationale accurately describe the worker disconnect loop fix.

@ericallam ericallam merged commit b65a04e into main May 1, 2026
43 checks passed
@ericallam ericallam deleted the fix/dev-worker-disconnect-loop branch May 1, 2026 16:11
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