From 4dbb4e2a701561f9bb688b019786ae8b2e636bf9 Mon Sep 17 00:00:00 2001 From: Gary Magyar Date: Mon, 2 Mar 2026 08:47:20 +0000 Subject: [PATCH] fix: guard createASTCache against zero maxSize to prevent LRU cache crash When a repo has no parseable files (e.g., unsupported languages or all files filtered out), chunks.reduce returns 0, causing createASTCache(0) to pass max:0 to LRUCache which throws TypeError. This clamps maxSize to at least 1 and adds a progress message when no parseable files exist. --- gitnexus/src/core/ingestion/ast-cache.ts | 5 +++-- gitnexus/src/core/ingestion/pipeline.ts | 9 +++++++++ gitnexus/test/unit/ast-cache.test.ts | 9 +++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/gitnexus/src/core/ingestion/ast-cache.ts b/gitnexus/src/core/ingestion/ast-cache.ts index 0ae105120f..011b9df65d 100644 --- a/gitnexus/src/core/ingestion/ast-cache.ts +++ b/gitnexus/src/core/ingestion/ast-cache.ts @@ -10,10 +10,11 @@ export interface ASTCache { } export const createASTCache = (maxSize: number = 50): ASTCache => { + const effectiveMax = Math.max(maxSize, 1); // Initialize the cache with a 'dispose' handler // This is the magic: When an item is evicted (dropped), this runs automatically. const cache = new LRUCache({ - max: maxSize, + max: effectiveMax, dispose: (tree) => { try { // NOTE: web-tree-sitter has tree.delete(); native tree-sitter trees are GC-managed. @@ -41,7 +42,7 @@ export const createASTCache = (maxSize: number = 50): ASTCache => { stats: () => ({ size: cache.size, - maxSize: maxSize + maxSize: effectiveMax }) }; }; diff --git a/gitnexus/src/core/ingestion/pipeline.ts b/gitnexus/src/core/ingestion/pipeline.ts index 8a7d9d0ee8..adfcabdfe5 100644 --- a/gitnexus/src/core/ingestion/pipeline.ts +++ b/gitnexus/src/core/ingestion/pipeline.ts @@ -108,6 +108,15 @@ export const runPipelineFromRepo = async ( const totalParseable = parseableScanned.length; + if (totalParseable === 0) { + onProgress({ + phase: 'parsing', + percent: 82, + message: 'No parseable files found — skipping parsing phase', + stats: { filesProcessed: 0, totalFiles: 0, nodesCreated: graph.nodeCount }, + }); + } + // Build byte-budget chunks const chunks: string[][] = []; let currentChunk: string[] = []; diff --git a/gitnexus/test/unit/ast-cache.test.ts b/gitnexus/test/unit/ast-cache.test.ts index 823982bc79..66cc6e3c3f 100644 --- a/gitnexus/test/unit/ast-cache.test.ts +++ b/gitnexus/test/unit/ast-cache.test.ts @@ -82,5 +82,14 @@ describe('ASTCache', () => { const defaultCache = createASTCache(); expect(defaultCache.stats().maxSize).toBe(50); }); + + it('clamps maxSize of 0 to 1 to prevent LRU cache error', () => { + const zeroCache = createASTCache(0); + expect(zeroCache.stats().maxSize).toBe(1); + // Should still function correctly + const tree = mockTree('test'); + zeroCache.set('a.ts', tree); + expect(zeroCache.get('a.ts')).toBe(tree); + }); }); });