Skip to content

fix(native): propagate edge/node write failures out of run_pipeline#1972

Open
carlos-alm wants to merge 1 commit into
fix/issue-1825-renamed-import-used-as-a-receiver-import-x-asfrom
fix/issue-1827-native-build-pipeline-silently-swallows-edge
Open

fix(native): propagate edge/node write failures out of run_pipeline#1972
carlos-alm wants to merge 1 commit into
fix/issue-1825-renamed-import-used-as-a-receiver-import-x-asfrom
fix/issue-1827-native-build-pipeline-silently-swallows-edge

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

run_pipeline (the fully-native build orchestrator, crates/codegraph-core/src/domain/graph/builder/pipeline.rs) silently discarded the Result from three edge/node-writing call sites, so a transaction that never started (or never committed) left the pipeline returning Ok(...) over an incomplete graph:

  • import_edges::insert_edges — on conn.unchecked_transaction() failure, previously did eprintln!(...); return; from a function returning (). Now returns Result<(), String>.
  • build_and_insert_call_edges (and its helper insert_call_edge_rows) — called without capturing/checking its return value at all. Now returns Result<(), String> and its caller propagates it.
  • Stage 5's insert_nodes::do_insert_nodes(...) call — was let _ = do_insert_nodes(...), explicitly discarding the Result. Now propagated via ?.

None of these bubbled up through run_pipeline's Result<BuildPipelineResult, String>, so NativeDatabase::build_graph() (the napi entry point) returned success to JS even when a transaction failed to start (e.g. transient SQLITE_BUSY under WAL contention). The only observable symptom was an easily-missed eprintln! to stderr.

Fix

Thread the Result from all three call sites up through run_pipeline via ?. NativeDatabase::build_graph already .map_errs run_pipeline's error into a thrown napi error, and tryNativeOrchestrator (the JS caller) already catches that and falls back to the JS/WASM pipeline — so a write failure now triggers a real retry instead of a silently-successful build over incomplete data.

insert_edges keeps its existing per-chunk resilience (one malformed chunk doesn't sacrifice the rest of the batch) but now also returns Err when any chunk failed, or when the transaction couldn't start/commit, instead of only warning to stderr.

Since commit_file_hashes runs after edge insertion, a write failure here now aborts the pipeline before file_hashes is touched — preserving the self-healing coupling from #1731 (an un-advanced hash means the next build retries the file), and now also making the current build's failure visible to the caller instead of reporting false success.

Test plan

  • cargo test --release (codegraph-core crate) — 532 passed, 0 failed
  • New unit tests in import_edges.rs for insert_edges: commits + returns Ok on success, returns Ok for an empty batch, and returns Err when the transaction can't start (regression test for this issue, using a nested BEGIN to force a deterministic transaction-start failure)
  • cargo clippy --release — no new warnings introduced by this change
  • Rebuilt the native addon locally (npx napi build --platform --release, codesigned), swapped it into node_modules/@optave/codegraph-darwin-arm64, and verified the native build lifecycle end-to-end: full build, no-op incremental rebuild, and single-file incremental rebuild all behave identically to before this change
  • npx vitest run tests/builder/pipeline.test.ts tests/integration/ — 915 passed (2 unrelated Lua/CLI-timeout failures confirmed to be resource-contention flakiness from the large parallel batch; both pass individually)
  • npm run lint — clean (no TS/JS files touched)
  • npm test (full suite) — 3679 passed; the only failures are 82 tests across verilog/objc/julia/dynamic-groovy, all traced via npm run doctor to 7 optional WASM grammars missing from this fresh worktree (a known, pre-existing, non-blocking environment gap — see src/infrastructure/doctor.ts, unrelated to this change)

Fixes #1827

Stacked on #1825/#1826 (base branch fix/issue-1825-renamed-import-used-as-a-receiver-import-x-as) — only the two Rust files touched by this issue's fix are in this diff.

insert_edges, build_and_insert_call_edges, and Stage 5's do_insert_nodes
call all discarded their Result via `let _ =` (or an unchecked () return
for insert_edges), so a transaction that never started or failed to
commit left run_pipeline returning Ok(...) over an incomplete graph.
NativeDatabase::build_graph then reported a "successful" build to JS
with nothing beyond a stderr eprintln to show for it.

Thread the Result through all three call sites via `?` so a write
failure aborts run_pipeline and surfaces as a thrown napi error.
tryNativeOrchestrator already catches that and falls back to the JS/WASM
pipeline, so this turns a silent partial build into a real retry.

Fixes #1827
@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR makes native graph write failures visible to callers. The main changes are:

  • Propagates node insertion failures out of run_pipeline.
  • Propagates import-edge insertion failures before committing file hashes.
  • Propagates call-edge insertion failures instead of discarding them.
  • Adds insert_edges tests for success, empty input, and transaction-start failure.

Confidence Score: 5/5

This looks safe to merge after checking the partial import-edge retry behavior.

  • The core error propagation is consistent with the intended fallback path.
  • One chunk-failure path can leave successful import-edge chunks committed before fallback rebuilds the graph.
  • The issue is conditional on a malformed chunk after the transaction starts.

crates/codegraph-core/src/domain/graph/builder/stages/import_edges.rs

Important Files Changed

Filename Overview
crates/codegraph-core/src/domain/graph/builder/pipeline.rs Propagates write errors from node, import-edge, and call-edge stages so native build failures reach the JS caller.
crates/codegraph-core/src/domain/graph/builder/stages/import_edges.rs Changes import-edge insertion to return errors, while still committing successful chunks before reporting chunk-level failure.

Fix All in Claude Code

Reviews (1): Last reviewed commit: "fix(native): propagate edge/node write f..." | Re-trigger Greptile

Comment on lines +596 to 603
return Err(format!(
"insert_edges: {} of {total_chunks} chunk(s) failed to insert: {}",
chunk_failures.len(),
chunk_failures.join("; ")
));
}
Ok(())
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Partial Edges Survive Failure

When one import-edge chunk fails, this path commits the successful chunks and only then returns Err. The native caller now throws and the JS fallback runs against the same database, so the fallback can see a graph that already contains some of the new import edges while the rest of the native stages were skipped; because the edge table has no content-level uniqueness, retry insertion can leave duplicate or mixed edge rows instead of a clean rebuild.

Fix in Claude Code

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.

1 participant