Conversation
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughAdds a process 'unhandledRejection' listener during extension activation that forwards the rejection reason to TelemetryService.sendTelemetryError("catchAllError") (telemetry errors are caught) and unregisters the listener on disposal. Tests verify routing, error property access, cleanup, and resilience when telemetry throws. ChangesUnhandled Rejection Telemetry Routing
Sequence Diagram(s)sequenceDiagram
participant Process
participant Extension
participant Telemetry
Process->>Extension: emit 'unhandledRejection' (reason)
Extension->>Telemetry: sendTelemetryError("catchAllError", reason)
Telemetry-->>Extension: returns or throws (caught)
Extension->>Process: process.off(handler) on dispose
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Bundle Size Reportdarwin-arm64: 74.2 MB
linux-x64: 75.9 MB
win32-x64: 76.8 MB
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In `@src/test/suite/unhandledRejectionRouting.test.ts`:
- Around line 45-123: Tests leak process 'unhandledRejection' listeners because
each test disposes context.subscriptions at the end of the test body, which
won't run if an assertion fails; move the cleanup into a shared afterEach so it
always runs: add an afterEach that iterates over context.subscriptions (or
stores/cleans attachments returned by attachUnhandledRejectionRouting) and calls
dispose() (or removes the process listener) to unhook the listener created by
attachUnhandledRejectionRouting; reference the existing
FakeContext/context.subscriptions and attachUnhandledRejectionRouting to locate
where to perform this guaranteed teardown.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 065e8916-dcf3-4c78-ba25-e9a1c5186e8e
📒 Files selected for processing (2)
src/dbtPowerUserExtension.tssrc/test/suite/unhandledRejectionRouting.test.ts
…r error data Production cluster `unhandlederror` with `Channel closed` (~45 unique machines on 0.61.x, mostly Win32) was opaque even after the `error.name`/`error.message`/`error.code` visibility fix in #1928 — that fix only applies to events emitted via our `sendTelemetryError` helper. Uncaught promise rejections fire VS Code's built-in `unhandlederror` event from `@vscode/extension-telemetry`'s `baseTelemetrySender.sendErrorData`, which only forwards `name` / `message` / `stack` and **drops `error.code`**. That's the field that distinguishes IPC failure modes (`ERR_IPC_CHANNEL_CLOSED`, `EPIPE`, `EBADF`, `ECONNRESET`, ...) — without it, every `Channel closed` event looks identical in App Insights regardless of root cause. Add a `process.on('unhandledRejection')` listener in `DBTPowerUserExtension.activate(context)` that routes the rejection reason through `this.telemetry.sendTelemetryError("unhandledRejection", reason)`. The helper already handles `error_name` / `error_message` / `error_code` extraction (`telemetry/index.ts:128-133`) plus the standard `dbtIntegrationMode` / `instanceName` / `localMode` envelope. The new event has a distinct name (`unhandledRejection`, camelCase) from VS Code's upstream `unhandlederror` (lowercase) so the two streams stay queryable separately in App Insights — both will fire for each uncaught rejection going forward, but the new one is enriched. Listener is registered as a `context.subscriptions` disposable so it unhooks on extension deactivation, preventing handler accumulation across reload cycles. Telemetry failures inside the listener are swallowed (try/catch) so a broken telemetry pipeline can't recursively re-enter the rejection path. Adds `src/test/suite/unhandledRejectionRouting.test.ts` with four cases covering (1) the routing happy path, (2) `error.code` propagation, (3) dispose unhooks the listener, and (4) a throwing telemetry stub doesn't re-enter the rejection path. Tests drive `process.emit` directly because Jest registers its own `unhandledRejection` listener for failure reporting, which interferes with letting a real `Promise.reject` bubble naturally. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6b7f7bc to
5aa0a9a
Compare
Address review feedback from @mdesmet on PR #1940. Aligns with the project-wide `-Error` suffix convention for `sendTelemetryError`'s first argument (e.g. `installDepsError`, `compileNodePythonError`, `extensionActivationError`). Updates the matching assertions in `unhandledRejectionRouting.test.ts`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
✅ Tests — All Passed |
Summary
Production cluster
unhandlederrorwithChannel closed(~45 unique machines on 0.61.x, mostly Win32) is opaque in App Insights even after theerror.name/error.message/error.codevisibility fix in #1928 — that fix only applies to events emitted via oursendTelemetryErrorhelper. Uncaught promise rejections fire VS Code's built-inunhandlederrorevent from@vscode/extension-telemetry'sbaseTelemetrySender.sendErrorData, which only forwardsname/message/stackand dropserror.code.error.codeis the field that distinguishes IPC failure modes (ERR_IPC_CHANNEL_CLOSED,EPIPE,EBADF,ECONNRESET, ...). Without it, everyChannel closedevent looks identical in App Insights regardless of root cause — making it impossible to triage which subsystem the IPC channel was attached to.What this PR does
Adds a
process.on('unhandledRejection')listener inDBTPowerUserExtension.activate(context)that routes the rejection reason throughthis.telemetry.sendTelemetryError("unhandledRejection", reason). The helper already handleserror_name/error_message/error_codeextraction (telemetry/index.ts:128-133) plus the standarddbtIntegrationMode/instanceName/localModeenvelope.Three deliberate properties:
unhandledRejection(camelCase) vs upstreamunhandlederror(lowercase) — both events stream to App Insights separately, so the new one is queryable independently and the upstream stream stays untouched. Lets us deprecate usingunhandlederrorlater without breaking existing dashboards.context.subscriptionsso the listener unhooks on extension deactivation. Without this, handlers accumulate one-per-reactivation across the lifetime of a VS Code window.What this enables
The next telemetry rotation will give us
error_codeon clusterunhandledRejection, which means we can split "Channel closed" into:ERR_IPC_CHANNEL_CLOSED— node child_process IPC torn down (this is the one tied to PR "Missing profile" error when opening a workspace #98's atomic-swap race inaltimate-dbt-integration)EPIPE— write to a closed pipeEBADF— bad file descriptor (file-handle leak, fixed in earlier PR Cannot use extension #64 but worth verifying)Today these are all anonymous
Error: Channel closedrows. Post-fix they become individually triageable.Test plan
Adds
src/test/suite/unhandledRejectionRouting.test.tswith four cases:npx jest— 32 of 33 suites pass; only failure is the pre-existingjupyterlabServicesCompatenvironmental issue (missing build artifact) confirmed unrelated by stashing this PR's changescontext.subscriptionsdisposable — verified by test 3 (no calls after dispose)process.emitdirectly because Jest's ownunhandledRejectionlistener for failure reporting interferes with lettingPromise.rejectbubble naturally — emitting the event is identical from the listener's perspective🤖 Generated with Claude Code
Summary by CodeRabbit
Improvements
Tests