Repository navigation
C/C++: a CRLF file with a backslash-continued string literal swallows the rest of the file, and every later function is missing from the graph #1546
Description
Version
codebase-memory-mcp 0.10.1
Platform
Windows (x64)
Install channel
GitHub release archive / install.sh / install.ps1
Binary variant
standard
What happened, and what did you expect?
When a C/C++ source file uses CRLF line endings and contains a string literal continued
with a backslash at end of line, the extractor loses the end of that literal. The
enclosing function is emitted as a single node spanning to end of file, and every
function defined after it is missing from the graph entirely.
The same file with LF line endings extracts correctly. Line endings are the only
difference between the two inputs below.
Expected: the same set of functions, with the same spans, regardless of line endings.
CRLF is the default checkout on Windows, so this affects a normally configured Windows
working tree.
Both conditions are required. CRLF alone is fine, and backslash continuation on LF is
fine. Backslash continuation is uncommon in modern C++, since adjacent string literal
concatenation is the usual idiom, which is probably why this has survived several
releases.
Reproduction
1. Code. Two directories, byte-identical apart from line endings. Because the bug is
byte-sensitive, here is a generator rather than a paste, so the exact bytes survive
copying (176 bytes LF, 196 bytes CRLF):
src = r'''void Head()
{
int h = 0;
}
void Middle()
{
const wchar_t* s = L"line 0\r\n\
line 1\r\n";
}
void TrailerOne()
{
int a = 1;
}
void TrailerTwo()
{
int b = 2;
}
'''
import os
for name, text in (('lf', src), ('crlf', src.replace('\n', '\r\n'))):
os.makedirs(name, exist_ok=True)
open(os.path.join(name, 'repro.cpp'), 'wb').write(text.encode())2. Commands. Index each directory and list the functions:
codebase-memory-mcp cli index_repository '{"repo_path":"<dir>"}'
codebase-memory-mcp cli query_graph '{"project":"<name>","query":"MATCH (n:Function) RETURN n.name, n.start_line, n.end_line"}'
3. Result vs expected.
| Line endings | Head | Middle | TrailerOne | TrailerTwo |
|---|---|---|---|---|
| LF (expected) | 1-4 | 6-10 | 12-15 | 17-20 |
| CRLF (actual) | 1-4 | 6-20 | missing | missing |
Middle absorbs the remaining 10 lines of the file, and the two functions defined after
it are absent from the graph. The whole-graph counts differ accordingly: LF indexes to 8
nodes / 7 edges, CRLF to 6 nodes / 5 edges. CBM_LSP_DISABLED=1 does not change the
outcome, so this looks like the base extractor rather than the Hybrid LSP layer.
One caveat when reproducing: index into a fresh cache directory. An incremental re-index
of an already-indexed project returns parse_partial_count: 0 even though the graph is
still missing the functions, which makes the flag look like it cleared when it has not.
Logs
On 0.10.1, `index_repository` on the CRLF directory reports `parse_partial_count: 1`,
and the per-project logfile carries:
parse_partial 8-12,12-12,20-20 repro.cpp
Releases before 0.10.0 reported this file clean, so the loss was fully silent. Since
0.10.0 the flag is raised, but the file is still not extracted.
Diagnostics trajectory (memory / performance / leak issues)
Project scale (if relevant)
No response
Confirmations
- I searched existing issues and this is not a duplicate.
- My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.
Activity
DeusData commentedon Aug 12, 2026
Thanks @noohi — this reproduces exactly as written, and the generator-not-a-paste was the right call for a byte-sensitive bug.
Current read: the C/C++ grammars we use are vendored generated parsers, so the line-splice handling that mishandles
\\+ CRLF lives either in the grammar itself or needs a normalization layer ahead of it. Normalizing\\r\\nbefore parsing is the obvious candidate, but it shifts byte offsets, so it has to be done with the span mapping in mind rather than as a blind substitution — which is why we're not rushing it into today's release.Queued for the next batch with that analysis attached. Your caveat about indexing into a fresh cache is noted too; an incremental re-index reporting
parse_partial_count: 0over a graph that is missing functions is arguably its own reporting bug.DeusData commentedon Sep 26, 2026
Thank you, @noohi, for the byte-exact generator. It's exactly what a line-ending bug needs!
I re-ran it (CRLF verified, with the backslash-continued literal) on current main and on the v0.11.0 release. Both copies now produce the same four functions with the same spans: Head, Middle, TrailerOne and TrailerTwo. I tested on macOS; the bug should be byte-level rather than OS-level, but could you re-run your two directories on Windows with v0.11.0 into a fresh cache? If the CRLF copy matches the LF copy for you too, we'll close this. If it doesn't, the platform difference is itself the next lead.
noohi commentedon Sep 26, 2026
Re-ran on Windows with v0.11.0 into a fresh cache: cache dir and daemon lock dirs deleted, daemon stopped before the run.
The CRLF copy still does not match the LF copy here.
Environment:
The fixture is the generator output from the report, re-generated today:
Driven via
cli --json index_repository --args-file ...thenquery_graphwithMATCH (n:Function) RETURN n.name AS name, n.start_line AS first, n.end_line AS last:So on Windows, Middle still spans to EOF and both trailers vanish.
If your CRLF copy on macOS hashes to the same value, the difference is in the Windows build or its file-read path rather than the input.
If it hashes differently, the bytes changed somewhere before the extractor saw them.
I can run a diagnostic build or a specific flag if that would narrow it.
github-actions commentedon Sep 26, 2026
Thanks for the report! To move this forward we need a bit more so we can reproduce it ourselves:
codebase-memory-mcp --versionyou're onOnce that's here we'll pick it straight back up. Heads-up: issues left
awaiting-reporterare automatically closed after a few weeks of silence, but a comment reopens the door anytime.noohi commentedon Sep 26, 2026
Everything the bot asks for is in the report and in my comment above, but here it is in one place.
Version:
codebase-memory-mcp 0.11.0, the official windows-amd64 release binary (SHA256 7edcd3807ebcfd85ec1968985964080f2589748da2fc3c7ce9261eebab31ff04).Public repro: the generator script in the original report, no proprietary code involved.
It writes two copies of the same 20-line C++ file,
lf/repro.cppandcrlf/repro.cpp.Expected hashes: crlf 57d03ea79302446a0da11fa762d9726412500f9573d94eef5a490ed95287b618, lf cdc0442b2345ea6da7b11e81fcdb82cb41fe883859d85d2d49be0aa2546ef141.
Steps on Windows 11 x64, fixture on a local NTFS disk:
codebase-memory-mcp cli --json index_repository --args-file args.jsonwith{"repo_path": "<dir>\\lf"}, then the same forcrlf.codebase-memory-mcp cli --json query_graph --args-file q.jsonwith{"project": "<project from step 2>", "query": "MATCH (n:Function) RETURN n.name AS name, n.start_line AS first, n.end_line AS last"}for each copy.Result: the lf copy gives Head, Middle, TrailerOne and TrailerTwo.
The crlf copy gives only Head and Middle, with Middle spanning lines 6-20 and parse_partial 1.
This still reproduces on v0.11.0 today, so please keep it open.
DeusData commentedon Oct 19, 2026
Thank you, @noohi, for the Windows 0.11.0 retest, matching release hash, and byte-exact fixtures. You already supplied what the bot requested — sorry for the redundant prompt. The Windows failure remains unresolved in this conversation; no further rerun is requested until there is a specific diagnostic or fix to test.
DeusData commentedon Oct 25, 2026
Thank you for the byte-exact Windows retest and release hash. You have already supplied the required evidence; the Windows-specific mismatch needs maintainer investigation before another rerun. Sorry for the delay; this is waiting on maintainer follow-up, and I’ll follow up soon.