# Issue #136: review_repository KeyError('\n    "title"') Root Cause & Fix

**Date**: 2026-04-03
**Issue**: review_repository MCP tool fails with "Unexpected error: '\n    \"title\"'"
**Affected review types**: ALL (security, architecture, performance, quality, testing, documentation)
**Reported on**: architecture review, large TypeScript monorepo (~179 files, 62.5K lines)

---

## Root Cause

All six review prompt templates in
`src/mcp_vector_search/analysis/review/prompts.py`
contain JSON few-shot examples that use literal `{` and `}` characters, e.g.:

```
```json
[
  {
    "title": "God Class: UserManager...",
    ...
  }
]
```
```

In `engine.py:_analyze_with_llm`, those templates were substituted using:

```python
prompt = prompt_template.format(
    code_context=code_context,
    kg_relationships=kg_context,
)
```

Python's `str.format()` scans the entire template string for `{...}` patterns
and treats the opening `{` of the JSON example object as the start of a
replacement field.  The content `\n    "title"` (newline + 4 spaces + `"title"`)
is what format() reads as the "field name" before it hits an unrecognised character
and raises:

```
KeyError('\n    "title"')
```

This exception propagates uncaught through `_analyze_with_llm` -> `run_review`
-> `handle_review_repository`, where the top-level `except Exception as e:` handler
converts it to:

```
"Unexpected error: '\n    \"title\"'"
```

### Why only report on architecture / large repos?

All review types are equally broken.  Architecture reviews were the first to be
reported because:
- Architecture is a common starting point for monorepo analysis
- Large repos have enough code to make the review valuable, so users try it first
- The error message is opaque enough that smaller tests might have been dismissed

---

## Fix

**File**: `src/mcp_vector_search/analysis/review/engine.py`

Replace `str.format()` with `str.replace()` for the two known placeholders:

```python
# Before (broken):
prompt = prompt_template.format(
    code_context=code_context,
    kg_relationships=kg_context,
)

# After (fixed):
prompt = prompt_template.replace("{code_context}", code_context).replace(
    "{kg_relationships}", kg_context
)
```

`str.replace()` performs a simple substring substitution and does not treat any
other characters as special, so the literal `{` and `}` in the JSON examples
pass through unchanged.

Note: `PR_REVIEW_PROMPT` in `pr_engine.py` was already correctly escaped using
`{{` and `}}` for its JSON example — that template was not affected.

---

## Tests Added

**File**: `tests/unit/review/test_review_engine.py`

Three new parametrized/standard tests (13 test cases total):

| Test | Purpose |
|------|---------|
| `test_prompt_template_substitution_does_not_raise[*]` | All 6 prompts work with `str.replace()` |
| `test_prompt_template_str_format_fails_without_fix[*]` | Documents that raw `str.format()` DOES fail (confirms the bug) |
| `test_analyze_with_llm_architecture_does_not_raise_key_error` | End-to-end: `_analyze_with_llm` returns `[]` for architecture without raising |

All 38 tests in the file pass.

---

## Files Changed

| File | Change |
|------|--------|
| `src/mcp_vector_search/analysis/review/engine.py` | Replace `str.format()` with `str.replace()` in `_analyze_with_llm` |
| `tests/unit/review/test_review_engine.py` | Add 13 regression test cases covering the fix |
