# Code-Graph-RAG Limitations: 9 Critical Constraints in the Multi-Language Analysis Tool

> Explore 9 critical limitations of Code-Graph-RAG for multi-language analysis. Understand constraints in language coverage, static analysis, and indexing for better retrieval accuracy.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: deep-dive
- Published: 2026-08-14

---

**Code-Graph-RAG (cgr) has nine major limitations spanning language coverage gaps, static analysis boundaries, Tree-sitter grammar quirks, and incremental indexing edge cases that constrain its retrieval accuracy and completeness.**

Code-Graph-RAG is an open-source knowledge graph builder that converts multi-language repositories into queryable Memgraph structures using Tree-sitter parsers. While it supports 14 programming languages without requiring compilation, its architecture introduces specific technical constraints that users must understand when evaluating retrieval results. This guide examines each limitation with direct references to the `vitali87/code-graph-rag` source code and evaluation framework.

---

## Language Coverage and Maturity Gaps

### Partial Language Support for Scala and Others

Code-Graph-RAG officially supports Python, TypeScript/TSX, JavaScript, Rust, Go, Java, C, C++, C#, PHP, Lua, Dart, Scala, and Ruby. However, not all languages reach the same maturity level.

According to the documented language support matrix, **Scala remains in active development** and some languages rely on a pluggable ast-grep tier that emits only a limited set of node types. This creates uneven graph fidelity across the supported language set.

```bash

# Enable typed-language mode for better recall in supported languages

cgr start --repo-path ./rust-demo --languages rust --typed

```

The `--typed` flag improves type inference where implemented, but falls back to simplified parsing for languages with limited native support.

---

## Name-Based Retrieval Oracle Constraints

### Simple Name Matching Creates Recall Gaps

The evaluation harness in [`evals/README.md`](https://github.com/vitali87/code-graph-rag/blob/main/evals/README.md) uses a **name-based oracle** that matches symbols solely by their simple identifier names. This design choice introduces deliberate boundaries on what cgr considers correct.

The oracle does **not count** several patterns:
- Anonymous arrow functions without explicit names
- Built-in name collisions across namespaces
- Constructor resolution when `__init__` is implicit

Multiple symbols sharing a common name (e.g., `process_data` in different modules) create ambiguity that the oracle cannot disambiguate, leading to defensible but incomplete recall measurements.

---

## Tree-Sitter Grammar Quirks and Pre-Processor Blindness

### C++ Grammar Drops Edges in Conditional Blocks

Tree-sitter's C++ grammar contains documented bugs affecting graph construction. Specifically, the parser **drops edges inside `#if`/`#endif` preprocessor blocks**, and the C grammar does not evaluate pre-processor conditionals at all.

This produces false positives and negatives in the resulting graph:

```c
#ifdef DEBUG
    void debug_log(const char* msg);  // Always appears in graph, even if DEBUG undefined
#endif

void production_code() {
    debug_log("test");  // Edge created even when DEBUG is undefined
}

```

The static analyzer cannot determine which conditional compilation branches are active without executing the pre-processor.

### Dead Code Persists in Graph

Because cgr's Tree-sitter front-end **does not execute** the C/C++ pre-processor, code conditionally compiled out still generates nodes and edges. These represent dead paths in the compiled binary but live paths in the knowledge graph.

The evaluation documentation notes this as an accepted trade-off for language-agnostic operation without requiring a full compiler toolchain.

---

## Dynamic and Runtime-Only Construct Blindness

### Static Analysis Cannot Capture Runtime Resolution

Code-Graph-RAG's graph construction is purely static. It cannot trace calls resolved through:
- Python's `getattr()` or `setattr()`
- JavaScript's dynamic property access
- Reflection APIs in Java, Go, or C#
- Dynamically generated or `eval`'d code

The evaluation framework explicitly measures this as a **recall ceiling** rather than a bug. Cgr intentionally omits these edges because they require runtime execution or sophisticated symbolic execution beyond the project's scope.

---

## Type Inference Gaps in Multiple Languages

### Limited to Local Variable Types

For Rust, Go, JavaScript, and Lua, cgr performs type inference only on **local variable types** within function scope. It does not implement full inter-procedural type inference across call boundaries.

This limitation manifests as missing `CALLS` edges when method receivers require type propagation through parameter passing or return values:

```rust
// Type inferred locally: OK
let processor: DataProcessor = DataProcessor::new();
processor.process();  // CALLS edge created

// Type from function return: may miss edge
let processor = create_processor();  // Return type not always propagated
processor.process();  // CALLS edge may be omitted

```

Per-language type inference implementations reside in `parsers/<language>/type_inference.py`. The Rust and Go modules specifically document this local-only scope limitation.

---

## Incremental Re-Indexing Edge Loss

### Inbound Edge Restoration Imperfections

When using incremental updates instead of full re-indexing, cgr updates only changed files. This creates edge synchronization challenges:

```bash

# Incremental update after editing a single file

cgr start --repo-path ./my_repo --incremental

```

During incremental processing:
- **Inbound edges** from unchanged callers to modified functions must be restored
- **Outbound edges** from modified functions may not fully re-resolve
- **Deep type-inference chains** across unchanged→changed boundaries diverge

The current implementation restores most inbound edges but retains a **small tail of divergence** for complex type-inference scenarios involving multiple file dependencies.

---

## Cross-Project Resolution Limitations

### Synthetic Fixtures vs. Real Monorepos

Code-Graph-RAG can resolve imports across top-level packages, but the evaluation harness validates this capability using **synthetic multi-package fixtures** rather than production-scale monorepos.

Real-world monorepos may expose edge-case bugs in:
- Circular dependency handling
- Version-conflict resolution across packages
- Namespace collision in merged graph schemas

The cross-project evaluation description in [`evals/README.md`](https://github.com/vitali87/code-graph-rag/blob/main/evals/README.md) lines 30-33 explicitly notes this coverage gap between demonstrated capability and battle-tested reliability.

---

## Performance vs. Fidelity Trade-Offs

### Compiler Front-End Alternative Reduces Recall

Cgr defaults to Tree-sitter parsing for speed. An alternative using full compiler front-ends (e.g., libclang for C++) improves edge fidelity but **reduces recall dramatically**:

| Approach | Speed | Edge Fidelity | Total Recall |
|----------|-------|-------------|--------------|
| Tree-sitter (default) | Fast | Moderate | Higher |
| libclang front-end | Slower | Higher | Lower |

The compiler front-end emits fewer total edges due to stricter semantic analysis, creating a tension between precision and coverage that users must navigate based on their retrieval requirements.

---

## Summary

- **Language coverage** spans 14 languages with uneven maturity; Scala and ast-grep-tier languages have reduced node type support
- **Name-based oracle** in evaluation deliberately excludes anonymous functions and certain collision patterns, bounding measurable recall
- **Tree-sitter grammar quirks** in C/C++ drop edges in pre-processor blocks without evaluating conditionals
- **Pre-processor blindness** causes dead code to appear as live graph edges
- **Dynamic constructs** (`getattr`, reflection, `eval`) are inherently invisible to static analysis
- **Type inference** is local-only for Rust, Go, JavaScript, and Lua, missing inter-procedural `CALLS` edges
- **Incremental re-indexing** restores most inbound edges but leaves residual divergence in deep type chains
- **Cross-project resolution** is validated on synthetic fixtures, not production monorepos
- **Tree-sitter vs. compiler front-end** presents a speed-fidelity-recall trade-off with no universally optimal setting

---

## Frequently Asked Questions

### Does Code-Graph-RAG require compilation to build the knowledge graph?

No. Code-Graph-RAG operates without compilation using Tree-sitter parsers, which enables its language-agnostic design. However, this same design choice causes the pre-processor blindness and dynamic construct limitations—trade-offs explicitly accepted for portability and speed according to the [`evals/README.md`](https://github.com/vitali87/code-graph-rag/blob/main/evals/README.md) documentation.

### Why does Code-Graph-RAG miss some function calls in my Rust project?

The Rust type inference implementation in [`parsers/rust/type_inference.py`](https://github.com/vitali87/code-graph-rag/blob/main/parsers/rust/type_inference.py) handles only local variable types, not inter-procedural propagation. If your code calls methods on values returned from functions without explicit type annotations, cgr may fail to resolve the receiver type and omit the `CALLS` edge. Enable `--typed` mode for best results, though full inter-procedural inference remains unimplemented.

### How accurate is incremental re-indexing compared to full re-indexing?

Incremental re-indexing restores most inbound edges from unchanged callers but maintains a documented "small tail of divergence" for complex type-inference scenarios. For critical analysis requiring complete accuracy, full re-indexing remains the safer choice despite longer processing time. The divergence is tracked in [`evals/README.md`](https://github.com/vitali87/code-graph-rag/blob/main/evals/README.md) lines 81-87 as a known operational constraint.

### Can Code-Graph-RAG handle dynamically typed languages like Python accurately?

Python receives among the most mature support, but dynamic resolution through `getattr`, `setattr`, or runtime code generation remains invisible to cgr's static analysis. The evaluation framework treats this as an inherent recall ceiling rather than a solvable bug—true dynamic edge detection would require runtime instrumentation beyond cgr's architecture.