Skip to content

fix(resolution): skip oversized files when chasing imports/re-exports — a single large asset import OOMs the whole index#1288

Open
li1164267803 wants to merge 1 commit into
colbymchenry:mainfrom
li1164267803:fix/resolution-oversized-file-oom
Open

fix(resolution): skip oversized files when chasing imports/re-exports — a single large asset import OOMs the whole index#1288
li1164267803 wants to merge 1 commit into
colbymchenry:mainfrom
li1164267803:fix/resolution-oversized-file-oom

Conversation

@li1164267803

Copy link
Copy Markdown

fix(resolution): skip oversized files when chasing imports/re-exports — a single large asset import OOMs the whole index

Problem

codegraph init dies with FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory during "Resolving refs", reproducibly at the same ref, on a mid-size React/TS project (1,161 files, ~54k refs). Raising the heap doesn't help — an 8 GB heap (NODE_OPTIONS=--max-old-space-size=8192, honored by the vendored runtime) dies at the same ~65%. The background daemon OOMs the same way on every session, so the project can't be indexed at all. Affects 1.3.1 and 1.4.1; 0.9.9 was fine.

Minimal repro — two files:

mkdir -p repro/src && cd repro && git init
dd if=/dev/urandom of=src/big.mp4 bs=1m count=240
printf 'import video from "./big.mp4";\nexport function play(): string {\n  return video;\n}\n' > src/app.ts
codegraph init   # OOM at "Resolving refs"

Root cause

Reference resolution follows import targets verbatim. An asset import — import Emilia from "@/assets/Emilia.mp4" in the real project (a 243 MB video), ./big.mp4 above — hands the resolver a path that extraction never parsed. Extraction has a MAX_FILE_SIZE guard (1 MB) precisely so vendored blobs and bundles stay out of the graph, but resolution's readFileCached() has no such guard: the import/re-export chase (getReExports, getImportMappings) reads the target whole, decodes hundreds of MB of binary as UTF-8, and regex-scans the resulting string for re-exports (for a non-JS extension the language re-key falls back to the consumer's language, so the mp4 bytes are scanned as TypeScript). The transient strings and match arrays blow through any heap limit. Since the file was never parsed, the read can never produce a resolution — the cost buys nothing.

Instrumentation pinpointing it (per-ref log, 2 GB heap): heap is at 110 MB entering the Emilia import ref, then the process dies inside that single resolveOne call.

Fix

Mirror extraction's guard in readFileCached(): stat before reading, and treat anything over 1 MB as unreadable (cached null). Oversized files become exactly as invisible to resolution as they already are to extraction — no behavior change for any file the graph can actually contain.

Tests

Two regression tests in __tests__/resolution-oversized-file.test.ts:

  • an oversized (>1 MB) but syntactically valid barrel must not have its re-export chain chased — pre-fix this produced a calls edge through a file the graph doesn't contain (that's the read the OOM rides in on); post-fix the chase stops at the barrel
  • a project importing a >1 MB binary asset indexes cleanly

Both fail before the fix and pass after. Full suite: 139 files, 2,374 passed / 4 skipped — green.

Verified end-to-end: the 240 MB two-file repro and the original 1,161-file project both index completely with a 2 GB heap post-fix (previously OOM'd 8 GB); index integrity confirmed (13,633 nodes / 31,397 edges — 232 more resolved edges than 0.9.9 produced on the same repo).

🤖 Generated with Claude Code

@li1164267803 li1164267803 force-pushed the fix/resolution-oversized-file-oom branch from 7d4f1a8 to 9d38b80 Compare July 15, 2026 03:38
Reference resolution follows import targets verbatim, so an asset import
(import video from "./intro.mp4") hands readFileCached() a path
extraction never parsed. The content can't answer an import/re-export
chase — extraction skips files over MAX_FILE_SIZE, so they have no
nodes — but resolution read them whole anyway, decoded them as UTF-8,
and regex-scanned them. On a real project a single ~240 MB video import
OOM'd an 8 GB heap at "Resolving refs" ~65%, killing every codegraph
init (reproducible with two files: one .ts importing one 240 MB binary).

Mirror extraction's guard in readFileCached(): stat first, and treat
anything over 1 MB as unreadable (cached null), making oversized files
as invisible to resolution as they already are to extraction.

Regression tests: an oversized barrel's re-export chain must not be
chased (pre-fix it produced a calls edge through a file the graph
doesn't contain), and a project importing a >1 MB binary must index.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@li1164267803 li1164267803 force-pushed the fix/resolution-oversized-file-oom branch from 9d38b80 to ecccb95 Compare July 15, 2026 03:45
colbymchenry

This comment was marked as outdated.

colbymchenry

This comment was marked as outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants