From e90c2c9d1b921792ec390281d709d87838e93282 Mon Sep 17 00:00:00 2001 From: barslev Date: Fri, 22 May 2026 14:34:25 +0200 Subject: [PATCH 1/2] fix(scan): surface tier1 finalize failures with a retry (REA-454) handleCreateNewScan was calling finalizeTier1Scan and discarding the CResult. When the finalize POST failed (transient 5xx, network blip, etc.) the CLI logged nothing and exited 0, while depscan's tier1 reachability scan row stayed in INIT forever. Investigation found >60k stuck INIT-with-metadata rows in coreweave that had a socket_cli_version set, confirming socket-cli was the lost finalizer. Capture the result, retry once on any failure, and surface a clear error including the scan id and tier1 reachability scan id if both attempts fail. Exit code is intentionally unchanged: the full scan upload above already succeeded, and finalize is metadata-only. --- src/commands/scan/handle-create-new-scan.mts | 22 ++++++++++++++++++- .../scan/handle-create-new-scan.test.mts | 5 ++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/commands/scan/handle-create-new-scan.mts b/src/commands/scan/handle-create-new-scan.mts index 6c76abc3c..5d92570ee 100644 --- a/src/commands/scan/handle-create-new-scan.mts +++ b/src/commands/scan/handle-create-new-scan.mts @@ -318,7 +318,27 @@ export async function handleCreateNewScan({ const scanId = fullScanCResult.ok ? fullScanCResult.data?.id : undefined if (reach && scanId && tier1ReachabilityScanId) { - await finalizeTier1Scan(tier1ReachabilityScanId, scanId) + // The finalize call is what transitions the tier1 reachability scan row + // out of INIT on depscan's side. Swallowing the failure here is what + // caused REA-454 (tens of thousands of rows stuck in INIT). Capture the + // result, retry once on any failure, and log clearly if it still fails. + // We deliberately do NOT change the user-facing exit code: the full scan + // upload above already succeeded, and finalize is metadata-only. + let finalizeResult = await finalizeTier1Scan( + tier1ReachabilityScanId, + scanId, + ) + if (!finalizeResult.ok) { + debugFn('warn', 'tier1 finalize failed, retrying once') + debugDir('inspect', { finalizeResult }) + finalizeResult = await finalizeTier1Scan(tier1ReachabilityScanId, scanId) + } + if (!finalizeResult.ok) { + logger.error( + `Failed to finalize tier1 reachability scan (scan_id=${scanId}, tier1_reachability_scan_id=${tier1ReachabilityScanId}): ${finalizeResult.message}${finalizeResult.cause ? ` — ${finalizeResult.cause}` : ''}`, + ) + debugDir('inspect', { finalizeResult }) + } } if (report && fullScanCResult.ok) { diff --git a/src/commands/scan/handle-create-new-scan.test.mts b/src/commands/scan/handle-create-new-scan.test.mts index 0998e9530..30ca607ef 100644 --- a/src/commands/scan/handle-create-new-scan.test.mts +++ b/src/commands/scan/handle-create-new-scan.test.mts @@ -7,6 +7,7 @@ import type { HandleCreateNewScanConfig } from './handle-create-new-scan.mts' const { mockFetchCreateOrgFullScan, mockFetchSupportedScanFileNames, + mockFinalizeTier1Scan, mockFindSocketYmlSync, mockGenerateAutoManifest, mockGetPackageFilesForScan, @@ -15,6 +16,7 @@ const { } = vi.hoisted(() => ({ mockFetchCreateOrgFullScan: vi.fn(), mockFetchSupportedScanFileNames: vi.fn(), + mockFinalizeTier1Scan: vi.fn(), mockFindSocketYmlSync: vi.fn(), mockGenerateAutoManifest: vi.fn(), mockGetPackageFilesForScan: vi.fn(), @@ -31,7 +33,7 @@ vi.mock('./fetch-supported-scan-file-names.mts', () => ({ })) vi.mock('./finalize-tier1-scan.mts', () => ({ - finalizeTier1Scan: vi.fn(), + finalizeTier1Scan: mockFinalizeTier1Scan, })) vi.mock('./handle-scan-report.mts', () => ({ @@ -126,6 +128,7 @@ describe('handleCreateNewScan excludePaths', () => { data: { size: 1 }, ok: true, }) + mockFinalizeTier1Scan.mockResolvedValue({ data: undefined, ok: true }) mockFindSocketYmlSync.mockReturnValue({ data: { parsed: { projectIgnorePaths: ['fixtures/**'] } }, ok: true, From 3826c2cff9167bf1bd19178db715b9b2bd69f011 Mon Sep 17 00:00:00 2001 From: barslev Date: Fri, 22 May 2026 14:38:22 +0200 Subject: [PATCH 2/2] Address review: drop REA-454 reference from inline comment --- src/commands/scan/handle-create-new-scan.mts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/commands/scan/handle-create-new-scan.mts b/src/commands/scan/handle-create-new-scan.mts index 5d92570ee..455ccc565 100644 --- a/src/commands/scan/handle-create-new-scan.mts +++ b/src/commands/scan/handle-create-new-scan.mts @@ -319,11 +319,11 @@ export async function handleCreateNewScan({ if (reach && scanId && tier1ReachabilityScanId) { // The finalize call is what transitions the tier1 reachability scan row - // out of INIT on depscan's side. Swallowing the failure here is what - // caused REA-454 (tens of thousands of rows stuck in INIT). Capture the - // result, retry once on any failure, and log clearly if it still fails. - // We deliberately do NOT change the user-facing exit code: the full scan - // upload above already succeeded, and finalize is metadata-only. + // out of INIT on depscan's side. Swallowing the failure here left tens + // of thousands of rows stuck in INIT. Capture the result, retry once on + // any failure, and log clearly if it still fails. We deliberately do + // NOT change the user-facing exit code: the full scan upload above + // already succeeded, and finalize is metadata-only. let finalizeResult = await finalizeTier1Scan( tier1ReachabilityScanId, scanId,