From 8e700957bb5fbaa9d1c518610070fc4db2c07a55 Mon Sep 17 00:00:00 2001 From: "posthog[bot]" <206114724+posthog[bot]@users.noreply.github.com> Date: Fri, 26 Jun 2026 15:22:13 +0000 Subject: [PATCH] fix: skip error-tracking capture for expected matched aborts A matched AbortCase (e.g. declining the GitHub connection in self-driving) is a deliberate, well-handled exit that already emits a distinct `agent aborted` product event. wizardAbort still reported it to error tracking via analytics.captureException, minting a spurious issue for every real declined connection. Add a `captureError` flag (default true) to wizardAbort and set it to `!matched` in the linear runner's ABORT branch, so matched aborts skip exception capture while the generic/unmatched abort and genuine faults still report. Generated-By: PostHog Code Task-Id: 7a9e916e-33bf-4ed3-968b-91d67e471235 --- src/__tests__/wizard-abort.test.ts | 25 +++++++++++++++++++++++++ src/lib/agent/runner/linear.ts | 5 +++++ src/utils/wizard-abort.ts | 14 ++++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/__tests__/wizard-abort.test.ts b/src/__tests__/wizard-abort.test.ts index 8a72e0cf..3d2b2bf1 100644 --- a/src/__tests__/wizard-abort.test.ts +++ b/src/__tests__/wizard-abort.test.ts @@ -118,6 +118,31 @@ describe('wizardAbort', () => { expect(mockAnalytics.captureException).not.toHaveBeenCalled(); }); + it('does not capture error when captureError is false (expected abort)', async () => { + const error = new WizardError('Agent aborted: github connection declined', { + integration: 'self-driving', + }); + + await expect(wizardAbort({ error, captureError: false })).rejects.toThrow( + 'process.exit called', + ); + + // The exception is suppressed for well-handled exits... + expect(mockAnalytics.captureException).not.toHaveBeenCalled(); + // ...but the run still shuts down so the rest of the flow is unaffected. + expect(mockAnalytics.shutdown).toHaveBeenCalled(); + }); + + it('captures error when captureError is true (default)', async () => { + const error = new Error('something broke'); + + await expect(wizardAbort({ error, captureError: true })).rejects.toThrow( + 'process.exit called', + ); + + expect(mockAnalytics.captureException).toHaveBeenCalledWith(error, {}); + }); + it('includes WizardError context in analytics capture', async () => { const error = new WizardError('MCP missing', { integration: 'nextjs', diff --git a/src/lib/agent/runner/linear.ts b/src/lib/agent/runner/linear.ts index 659dcbcd..b340386c 100644 --- a/src/lib/agent/runner/linear.ts +++ b/src/lib/agent/runner/linear.ts @@ -197,6 +197,11 @@ export async function runLinearProgram( error_type: AgentErrorType.ABORT, reason, }), + // A matched AbortCase is a deliberate, well-handled exit (e.g. the user + // declining the GitHub connection in self-driving). Skip error-tracking + // capture for these — the `agent aborted` product event above already + // records them. Reserve captureException for the generic/unmatched abort. + captureError: !matched, }); } diff --git a/src/utils/wizard-abort.ts b/src/utils/wizard-abort.ts index fe844728..3011a654 100644 --- a/src/utils/wizard-abort.ts +++ b/src/utils/wizard-abort.ts @@ -27,6 +27,15 @@ interface WizardAbortOptions { outroData?: OutroData; error?: Error | WizardError; exitCode?: number; + /** + * Whether to report `error` to error tracking via + * `analytics.captureException`. Defaults to `true`. Set `false` for + * expected, well-handled exits (e.g. a matched `AbortCase` such as a + * user declining the GitHub connection) so they don't mint spurious + * error-tracking issues. The product event (`wizardCapture`) is emitted + * separately by the caller regardless. + */ + captureError?: boolean; } const cleanupFns: Array<() => void> = []; @@ -59,6 +68,7 @@ export async function wizardAbort( outroData, error, exitCode = 1, + captureError = true, } = options ?? {}; logToFile(`[wizard-abort] exitCode=${exitCode}, message: ${message}`); @@ -69,8 +79,8 @@ export async function wizardAbort( // 1. Run registered cleanup functions runCleanups(); - // 2. Capture error in analytics (if provided) - if (error) { + // 2. Capture error in analytics (if provided and not an expected abort) + if (error && captureError) { analytics.captureException(error, { ...((error instanceof WizardError && error.context) || {}), });