From 2a9dfc810a2534a4da82e51cf2c68b05c106219d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 12:09:48 +0000 Subject: [PATCH 1/3] fix(dsn-cache): wrap JSON.parse calls in getCachedDetection with try/catch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getCachedDetection() called JSON.parse() on three stored JSON columns (source_mtimes_json, dir_mtimes_json, all_dsns_json) without error handling. If any stored JSON was corrupt (partial write, manual DB edit, schema mismatch), the unhandled SyntaxError would crash DSN detection entirely instead of treating it as a cache miss. The same file already handles this correctly for allResolved parsing (line 121-127), and repo-cache.ts uses the same pattern (line 55-67). Root cause: Missing try/catch around JSON.parse of cached data. Trigger: Any corruption in the dsn_cache table's JSON columns. Co-authored-by: Miguel Betegón --- src/lib/db/dsn-cache.ts | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/lib/db/dsn-cache.ts b/src/lib/db/dsn-cache.ts index 2dddd478c..c54943b81 100644 --- a/src/lib/db/dsn-cache.ts +++ b/src/lib/db/dsn-cache.ts @@ -362,32 +362,33 @@ export async function getCachedDetection( return; } - // Parse source mtimes and validate - const sourceMtimes = JSON.parse(row.source_mtimes_json) as Record< - string, - number - >; + let sourceMtimes: Record; + let dirMtimes: Record; + let allDsns: DetectedDsn[]; + try { + sourceMtimes = JSON.parse(row.source_mtimes_json) as Record; + dirMtimes = row.dir_mtimes_json + ? (JSON.parse(row.dir_mtimes_json) as Record) + : {}; + allDsns = JSON.parse(row.all_dsns_json) as DetectedDsn[]; + } catch { + recordCacheHit("dsn-detection", false); + return; + } + if (!(await validateSourceMtimes(projectRoot, sourceMtimes))) { recordCacheHit("dsn-detection", false); return; } - // Parse directory mtimes and validate (if present) - const dirMtimes = row.dir_mtimes_json - ? (JSON.parse(row.dir_mtimes_json) as Record) - : {}; if (!(await validateDirMtimes(projectRoot, dirMtimes))) { recordCacheHit("dsn-detection", false); return; } recordCacheHit("dsn-detection", true); - // Cache is valid - update last access time touchCacheEntry("dsn_cache", "directory", projectRoot); - // Parse and return cached detection - const allDsns = JSON.parse(row.all_dsns_json) as DetectedDsn[]; - return { fingerprint: row.fingerprint, allDsns, From 761f06a9dce4ca558d818f0371e0d311b5b56fb1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 12:09:55 +0000 Subject: [PATCH 2/3] fix(pagination): handle corrupted cursor_stack JSON gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getPaginationState() called JSON.parse(row.cursor_stack) without error handling. A corrupt cursor_stack value would throw an unhandled SyntaxError, crashing any pagination command (list issues, traces, etc.) instead of resetting pagination state. Now corrupted rows are deleted and treated as fresh state (same as expired entries), so the user gets a clean first-page result. Root cause: Missing try/catch around JSON.parse of stored pagination data. Trigger: Corrupted cursor_stack JSON in the pagination_cursors table. Co-authored-by: Miguel Betegón --- src/lib/db/pagination.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/db/pagination.ts b/src/lib/db/pagination.ts index c702918d9..c51029fd7 100644 --- a/src/lib/db/pagination.ts +++ b/src/lib/db/pagination.ts @@ -84,7 +84,15 @@ export function getPaginationState( return; } - const stack = JSON.parse(row.cursor_stack) as string[]; + let stack: string[]; + try { + stack = JSON.parse(row.cursor_stack) as string[]; + } catch { + db.query( + "DELETE FROM pagination_cursors WHERE command_key = ? AND context = ?" + ).run(commandKey, contextKey); + return; + } return { stack, index: row.page_index }; } From fcfaaf5efcd8c4f7e9f0d3105cab844b1ebb7e7d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 12:10:03 +0000 Subject: [PATCH 3/3] fix(http): add debug logging to silent catch blocks in sentry-client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three catch blocks in sentry-client.ts silently swallowed errors with empty handlers, violating the project's catch-block logging rule (see AGENTS.md). This made debugging impossible for: 1. Token refresh failures after 401 responses (handleUnauthorized) 2. Response cache write failures (cacheResponse) 3. Post-mutation cache invalidation failures (invalidateAfterMutation) Added log.debug() calls in all three catch blocks. The errors remain non-fatal (not re-thrown) but are now visible in debug output. Root cause: Empty catch blocks hiding operational failures. Trigger: Any error in token refresh, cache write, or cache invalidation. Co-authored-by: Miguel Betegón --- src/lib/sentry-client.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/sentry-client.ts b/src/lib/sentry-client.ts index c09bf48e5..cb26752a5 100644 --- a/src/lib/sentry-client.ts +++ b/src/lib/sentry-client.ts @@ -187,8 +187,8 @@ async function handleUnauthorized(headers: Headers): Promise { headers.set(RETRY_MARKER_HEADER, "1"); return true; } - } catch { - // Token refresh failed + } catch (error) { + log.debug("Token refresh failed after 401", error); } return false; } @@ -419,8 +419,8 @@ function cacheResponse( fullUrl, requestHeaders, response.clone() as Response - ).catch(() => { - // Non-fatal: cache write failures don't affect the response + ).catch((error) => { + log.debug("Response cache write failed", error); }); } @@ -447,8 +447,8 @@ async function invalidateAfterMutation( await Promise.all( prefixes.map((prefix) => invalidateCachedResponsesMatching(prefix)) ); - } catch { - /* best-effort: mutation already succeeded upstream */ + } catch (error) { + log.debug("Post-mutation cache invalidation failed", error); } }