Skip to content

Native build pipeline silently swallows edge-insertion failures (no error propagation) #1827

Description

@carlos-alm

Found while investigating #1731

While root-causing #1731 (file_hashes/edges coupling), I found a broader, distinct robustness gap in the native (Rust) build pipeline: several edge-writing functions swallow their own DB errors instead of propagating them, so run_pipeline can return Ok(...) (and the JS caller sees a "successful" build) even when edges were never written for some or all files.

Concrete evidence in crates/codegraph-core/src/domain/graph/builder/:

  • stages/import_edges.rs::insert_edges — on conn.unchecked_transaction() failure, does eprintln!(...); return; from a function returning (). The caller (pipeline.rs Stage 7) has no way to know this happened.
  • pipeline.rsbuild_and_insert_call_edges(conn, &file_symbols, &import_ctx, ...) is called without capturing/checking its return value at all.
  • pipeline.rs Stage 5 — let _ = insert_nodes::do_insert_nodes(conn, &insert_batches, &change_result.removed); explicitly discards the Result.

None of these failures bubble up through run_pipeline's Result<BuildPipelineResult, String>, so NativeDatabase::build_graph() (the napi entry point) returns success to JS even when a transaction failed to start (e.g. transient SQLITE_BUSY under WAL contention, which this codebase's own dual-connection dance can plausibly trigger). The only observable symptom is an eprintln! to stderr that most callers never see.

This is a different bug from #1731 (which was about timing — file_hashes committed before edges even had a chance to run). This one is about error visibility — even when the pipeline tries to write edges and fails, nothing tells the caller. Combined with file_hashes semantics (fixed in #1731 by deferring the hash commit past edge-building), a silent edge-write failure here would still leave file_hashes un-advanced on retry (self-healing), but the current build's result would incorrectly report success with incomplete data, and users get no diagnostic beyond an easily-missed stderr line.

Suggested fix (not done here — separate concern)

Thread Result (or at least abool/error-count) from insert_edges, build_and_insert_call_edges, and the Stage 5 do_insert_nodes call up through run_pipeline, so a transaction-start failure surfaces as a thrown JS error (matching how other stages already do via ?) instead of a silently-successful build.

Not fixing this now because it requires restructuring return types across several Rust functions — a larger, separate change from #1731's scope.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions