# How to Troubleshoot Graph Build Failures or Missing Nodes in Code-Review-Graph

> Troubleshoot graph build failures and missing nodes in code-review-graph. Learn to identify parser errors, path issues, schema problems, and cache staleness with CLI commands and API calls.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: how-to-guide
- Published: 2026-08-06

---

**Graph build failures and missing nodes in code-review-graph typically stem from parser errors, unnormalized Windows paths, outdated SQLite schemas, unresolved bare edges, or stale NetworkX caches, each identifiable through specific CLI commands or API calls.**

The code-review-graph (CRG) tool constructs a SQLite-backed knowledge graph from source code using tree-sitter parsers, but builds can fail silently or omit expected nodes when path normalization, schema migrations, or edge resolution steps encounter errors. Understanding how to troubleshoot graph build failures or missing nodes in code-review-graph requires inspecting the parser output, database schema version, and in-memory cache state to isolate whether the issue originates during parsing, storage, or retrieval.

## Verify the Parser and Language Support

The first failure point occurs when **tree-sitter** cannot parse a source file or a language plugin is missing. CRG extracts `NodeInfo` and `EdgeInfo` objects through the parser defined in [`code_review_graph/parser.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/parser.py).

Run the parser directly on a problematic file to expose language registration errors:

```bash
crg parse src/example.py --debug

```

If the output contains "Skipping … unsupported language", install the missing tree-sitter grammar. The language registration block near the top of [`parser.py`](https://github.com/tirth8205/code-review-graph/blob/main/parser.py) raises a `RuntimeError` when required parsers are absent, halting the build for that file.

## Check Path Normalization and Bridging

**Path normalization** failures cause `graph.get_node()` to return `None` even when the file exists. CRG stores all paths as POSIX strings, but Windows-style paths (`C:\proj\src\Foo.java`) require bridging.

The `normalize_file_path()` function in [`code_review_graph/parser.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/parser.py) converts back-slashes to forward-slashes during ingestion. During lookup, `_bridge_qualified_name()` in [`code_review_graph/graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph.py) (lines 58-66) handles the conversion.

Test the bridging mechanism manually:

```python
from code_review_graph.graph import GraphStore

gs = GraphStore("tmp/graph.db")

# Windows paths are bridged internally to POSIX keys

node = gs.get_node("C:\\proj\\src\\Foo.java::Bar")
print(node)  # Should return the node if bridging succeeds

```

If bridging fails, inspect the `normalize_file_path` implementation in [`parser.py`](https://github.com/tirth8205/code-review-graph/blob/main/parser.py) to ensure it correctly replaces back-slashes with forward-slashes before storage.

## Confirm Schema Version and Run Migrations

Outdated SQLite schemas cause SQL errors or missing tables like `nodes_fts` and `metadata`. CRG tracks schema versions in the `metadata` table, read by `get_schema_version()` in [`code_review_graph/migrations.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/migrations.py).

Migrations apply automatically in `GraphStore.__init__` (lines 98-107), but manual verification helps diagnose stale databases:

```python
gs = GraphStore("tmp/graph.db")
print(gs.get_metadata("schema_version"))

```

Compare the output against the `MIGRATIONS` list in [`migrations.py`](https://github.com/tirth8205/code-review-graph/blob/main/migrations.py). If the version is lower than the latest, call `run_migrations(gs._conn)` manually, or delete the database file to start fresh with the current schema.

## Resolve Bare Edge Identifiers

**Bare edges** occur when `CALLS` or `TESTED_BY` edges contain raw identifiers without fully-qualified names (missing `::` separators). These edges carry `bare_call_target` or `bare_tested_by_source` keys in their `extra` JSON column.

The resolution logic resides in `GraphStore._resolve_bare_endpoints()` (lines 665-885) in [`graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph.py). Inspect raw edges to identify unresolved targets:

```python
for edge in gs.iter_edges_by_source("src/example.py::main"):
    print(edge.kind, edge.source_qualified, edge.target_qualified, edge.extra)

```

If `extra` contains `"bare_call_target": "helper"`, run the CLI resolution commands:

```bash
crg resolve-bare-calls
crg resolve-bare-tested-by

```

These commands invoke `resolve_bare_edge_targets()`, converting bare identifiers into fully-qualified names like `module::function`.

## Invalidate Stale NetworkX Caches

CRG caches the NetworkX graph in `_nxg_cache` to speed up queries. Write operations like `store_file_nodes_edges()` call `_invalidate_cache()` (lines 21-25) to clear this cache, but a crash before completion leaves stale data.

Force a cache refresh:

```python
gs._nxg_cache = None  # Clear manually

```

Alternatively, reinstantiate `GraphStore` to rebuild the cache from the SQLite database on the next access.

## Rebuild Full-Text Search Indexes

Missing **FTS5** indexes prevent keyword searches from returning results. The `search_nodes()` function in [`code_review_graph/graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph.py) (lines 103-118) queries the `nodes_fts` virtual table, falling back to `LIKE` when the table is absent.

Rebuild the index using:

```bash
crg rebuild-fts

```

This executes SQL that drops and recreates the virtual table:

```python
gs._conn.executescript(
    "DROP TABLE IF EXISTS nodes_fts; CREATE VIRTUAL TABLE nodes_fts USING fts5(name, qualified_name, content='nodes');"
)
gs._conn.execute(
    "INSERT INTO nodes_fts(rowid, name, qualified_name) SELECT id, name, qualified_name FROM nodes;"
)
gs._conn.commit()

```

## Examine Build Logs and Run Diagnostics

The CLI driver in [`code_review_graph/cli.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/cli.py) uses the logger defined at the top of [`graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph.py). Increase verbosity to capture `ERROR` and `WARNING` entries tagged with `GRAPH_BUILD`:

```bash
crg build --log-level DEBUG

```

For a comprehensive check, run the diagnostic command:

```bash
crg diagnose

```

This validates:
- Database schema version against `MIGRATIONS`
- Presence of `nodes_fts` table
- Existence of at least one node via `has_nodes()`
- Absence of unresolved bare edges via `_resolve_bare_endpoints()`

## Complete Troubleshooting Workflow

Combine these steps in sequence to isolate failures:

```bash

# 1. Clean slate

crg clean-db

# 2. Rebuild with debug logging

crg build --all --log-level DEBUG

# 3. Resolve incomplete edges

crg resolve-bare-calls
crg resolve-bare-tested-by

# 4. Rebuild search indexes

crg rebuild-fts

# 5. Verify health

crg diagnose

```

Programmatically inspect specific files:

```python
from code_review_graph.graph import GraphStore
from code_review_graph.parser import parse_file

gs = GraphStore("tmp/graph.db")

# Parse and store atomically

nodes, edges = parse_file("src/main/java/Foo.java")
gs.store_file_nodes_edges("src/main/java/Foo.java", nodes, edges, fhash="abc123")

# Resolve any remaining bare edges

resolved = gs.resolve_bare_edge_targets()
print(f"Resolved {resolved} CALL edges")

# Verify node exists (handles Windows paths automatically)

node = gs.get_node(r"C:\proj\src\main\java\Foo.java::Bar")
print(node)

```

## Summary

- **Parser failures** indicate missing tree-sitter language grammars or unsupported file types; debug with `crg parse --debug`.
- **Path mismatches** between Windows and POSIX formats cause missing nodes; verify `normalize_file_path()` and `_bridge_qualified_name()` behaviors.
- **Schema mismatches** produce SQL errors; check `get_schema_version()` and run `crg diagnose` to validate migrations.
- **Bare edges** leave connections unresolved; use `crg resolve-bare-calls` and inspect `extra` metadata in [`graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph.py).
- **Stale caches** show outdated graph states; clear `_nxg_cache` manually or reinstantiate `GraphStore`.
- **Missing FTS indexes** break keyword search; rebuild with `crg rebuild-fts` when `nodes_fts` is absent.

## Frequently Asked Questions

### Why does `get_node()` return None for files that exist in the repository?

This occurs when **path normalization** fails to bridge Windows-style paths to POSIX keys. CRG stores paths using forward slashes via `normalize_file_path()` in [`parser.py`](https://github.com/tirth8205/code-review-graph/blob/main/parser.py), but lookups from Windows environments may pass back-slashes. The `_bridge_qualified_name()` method in [`graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph.py) (lines 58-66) handles this conversion; if it fails, verify that the stored path keys match the lookup format.

### How do I fix unresolved CALLS edges after building the graph?

Unresolved edges contain **bare identifiers** in their `extra` JSON field, such as `{"bare_call_target": "helper"}`. Run `crg resolve-bare-calls` to invoke `GraphStore._resolve_bare_endpoints()`, which maps these identifiers to fully-qualified names. Check for remaining bare edges by iterating through `iter_edges_by_source()` and inspecting the `edge.extra` dictionary.

### What causes the `nodes_fts` table missing error?

The **FTS5 virtual table** is created during schema migrations; if the database predates the FTS feature or migrations failed, the table is absent. Run `crg rebuild-fts` to recreate it, or delete the database file to trigger a fresh migration sequence when you next instantiate `GraphStore`.

### Why are my keyword searches returning empty results despite nodes existing in the database?

Searches rely on the `nodes_fts` table populated by the migration system. If the table exists but is empty, or if the schema version is outdated, `search_nodes()` falls back to slow `LIKE` queries that may miss data. Rebuild the index with `crg rebuild-fts` and verify the schema version using `gs.get_metadata("schema_version")` to ensure the FTS infrastructure is current.