Skip to content

[RED TEAM] PR #111 batch SQLite write fails with UNIQUE constraint on graph_nodes.node_id — graph left empty #114

Description

@Wolfvin

Temuan

PR #111 (RAM-first indexing) rewrote populate_graph_tables() in scripts/graph_model.py to do a single BEGIN EXCLUSIVE transaction that DELETEs all graph_nodes then executemany INSERTs the rows from the flat backend.json registry. The INSERT uses plain INSERT INTO graph_nodes ... with no OR IGNORE / OR REPLACE and no dedup of node_rows before insert.

If backend.json contains even one duplicate node_id (which happens routinely on real codebases — see reproduce below), the entire executemany raises sqlite3.IntegrityError: UNIQUE constraint failed: graph_nodes.node_id. The code catches this, issues ROLLBACK, and returns {"nodes": 0, "edges": 0}. The rollback also discards the DELETE FROM graph_nodes, so on a fresh scan the table ends up empty.

Net effect: after a normal codelens scan, graph_nodes has 0 rows and graph_edges contains only IMPORTS edges (added later by refine_call_edges). All CALLS edges are missing. Every downstream consumer of the graph (arch-metrics, callgraph, impact, dependents, graph-schema) silently returns empty/wrong results.

The warning surfaces to stderr but the scan still reports "status": "ok", masking the failure.

Cara reproduce

# Fresh clone, fully installed deps (tree-sitter + tree-sitter-python etc.)
mkdir -p /tmp/cl && cd /tmp/cl
git clone https://github.com/Wolfvin/CodeLens.git
cd CodeLens
pip install -e .
pip install tree-sitter-python tree-sitter-javascript tree-sitter-html tree-sitter-css tree-sitter-rust tree-sitter-typescript

PYTHONUTF8=1 python scripts/codelens.py init .
PYTHONUTF8=1 python scripts/codelens.py scan .

Then inspect the SQLite DB:

python -c "
import sqlite3, json
conn = sqlite3.connect('.codelens/codelens.db')
print('graph_nodes:', conn.execute('SELECT COUNT(*) FROM graph_nodes').fetchone()[0])
print('graph_edges:', conn.execute('SELECT COUNT(*) FROM graph_edges').fetchone()[0])
print('edge_types:', conn.execute('SELECT edge_type, COUNT(*) FROM graph_edges GROUP BY edge_type').fetchall())
backend = json.load(open('.codelens/backend.json'))
ids = [n['id'] for n in backend['nodes']]
from collections import Counter
dups = [(k,v) for k,v in Counter(ids).items() if v > 1]
print('duplicate node_ids in flat registry:', dups[:3])
print('flat_nodes total:', len(ids), 'unique:', len(set(ids)))
"

Output aktual

[codelens] WARNING: populate_graph_tables: batch write error: UNIQUE constraint failed: graph_nodes.node_id
{
  "status": "ok",
  ...
  "backend": { "nodes": 3595, "edges": 38441 },
  "graph":   { "nodes": 0,     "edges": 2810 },
  ...
}

# DB inspection
graph_nodes: 0
graph_edges: 2810
edge_types:   [('IMPORTS', 2810)]          # ← CALLS edges are entirely missing
duplicate node_ids in flat registry: [('scripts/smell_engine.py:511', 2)]
flat_nodes total: 3595 unique: 3594

The duplicate comes from scripts/smell_engine.py line 511, which is a code comment (# Scala: def name(), private def name()) that the smell-engine regex matches as TWO function declarations named name. Any codebase with a similar comment-regex collision triggers the bug.

Downstream symptom — codelens arch-metrics returns:

{
  "status": "ok",
  "metrics": [],
  "total_modules": 0,
  "god_modules": [],
  ...
}

...because arch_metrics._compute_metrics queries SELECT DISTINCT file FROM graph_nodes and the table is empty.

Output yang diharapkan

  • The batch write must handle duplicate node_ids gracefully (e.g. INSERT OR IGNORE, INSERT OR REPLACE, or dedup node_rows in Python before insert).
  • After scan, graph_nodes should contain the unique node count (3594 in this repro) and graph_edges should contain CALLS + IMPORTS edges.
  • arch-metrics should return real module metrics, not an empty list.
  • The scan output "graph": {"nodes": 0, ...} is currently lying — it should reflect the true count.

Severity

CRITICAL — every downstream graph-based command (arch-metrics, callgraph, impact, dependents, graph-schema) is silently broken on any codebase that produces a single duplicate node_id. The bug is reproducible on CodeLens's own codebase.

PR terkait

#111 (RAM-first indexing — closes #10)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions