What version of Kimi Code is running?
Reproduced on main at commit d1a46db94 (package @moonshot-ai/tree-sitter-bash).
Which open platform/subscription were you using?
N/A — this is a code-level correctness bug in the @moonshot-ai/tree-sitter-bash parser, found by source review and reproduced with a unit test. It is not tied to a plan or model.
Which model were you using?
N/A
What platform is your computer?
macOS (Darwin arm64). The bug is platform-independent — the parser is pure TypeScript.
What issue are you seeing?
parse() degrades a perfectly valid command whose scan range ends in a lone backslash into a single whole-source ERROR node (hasError: true), losing all structure.
On current main:
parse('echo \') returns a root that is a single ERROR [0,6) instead of command(command_name(word "echo"), word "\"). Internally SyntaxNodeBuilder throws RangeError: invalid node range [0, 7) for source of length 6, and parse()'s last-resort catch in src/parse.ts converts that into the whole-source ERROR node.
- The same happens for
\, [[ -f x && \, and case x in a\.
What steps can reproduce the bug?
In packages/tree-sitter-bash:
import { parse } from '#/parse';
const r = parse('echo \\'); // the string: echo, space, one backslash
// expected: ok:true, hasError:false — a command with a trailing word "\"
// actual: ok:true, hasError:true — root is a single whole-source ERROR node
This is not fuzzer-only: test/fixtures/corpus/statements.txt already contains a real line ending in a continuation backslash, so any consumer that parses a single line before the following newline arrives hits it.
What is the expected behavior?
A lone trailing backslash is kept as word text — which is exactly what scanWord's own comment in src/lexer.ts says should happen. echo \ should parse to a normal command with a trailing word "\" and hasError: false; the test-command and case inputs should error-recover locally rather than collapsing the whole tree.
Additional information
Root cause: four character scanners skip an escaped character with idx += 2. When the backslash is the last character of the scan range, that overshoots to end + 1, so the emitted node endIndex exceeds the source length (or the parent's end) and SyntaxNodeBuilder rejects it. The four sites are scanWord (src/lexer.ts) and scanCasePatternEnd / scanTestToken / scanTestCloser (src/parser.ts).
Proposed fix: clamp the skip to the scan bound, idx = Math.min(idx + 2, end). The clamp is a no-op unless the backslash is the final character, so no other parse changes. I verified locally that the full tree-sitter-bash suite — including the differential comparison against the reference tree-sitter-bash corpus — stays green, and added regression cases that fail before the change and pass after it.
I'm happy to open the PR once this is approved.
Contribution
What version of Kimi Code is running?
Reproduced on
mainat commitd1a46db94(package@moonshot-ai/tree-sitter-bash).Which open platform/subscription were you using?
N/A — this is a code-level correctness bug in the
@moonshot-ai/tree-sitter-bashparser, found by source review and reproduced with a unit test. It is not tied to a plan or model.Which model were you using?
N/A
What platform is your computer?
macOS (Darwin arm64). The bug is platform-independent — the parser is pure TypeScript.
What issue are you seeing?
parse()degrades a perfectly valid command whose scan range ends in a lone backslash into a single whole-sourceERRORnode (hasError: true), losing all structure.On current
main:parse('echo \')returns a root that is a singleERROR [0,6)instead ofcommand(command_name(word "echo"), word "\"). InternallySyntaxNodeBuilderthrowsRangeError: invalid node range [0, 7) for source of length 6, andparse()'s last-resortcatchinsrc/parse.tsconverts that into the whole-sourceERRORnode.\,[[ -f x && \, andcase x in a\.What steps can reproduce the bug?
In
packages/tree-sitter-bash:This is not fuzzer-only:
test/fixtures/corpus/statements.txtalready contains a real line ending in a continuation backslash, so any consumer that parses a single line before the following newline arrives hits it.What is the expected behavior?
A lone trailing backslash is kept as word text — which is exactly what
scanWord's own comment insrc/lexer.tssays should happen.echo \should parse to a normal command with a trailingword "\"andhasError: false; the test-command and case inputs should error-recover locally rather than collapsing the whole tree.Additional information
Root cause: four character scanners skip an escaped character with
idx += 2. When the backslash is the last character of the scan range, that overshoots toend + 1, so the emitted nodeendIndexexceeds the source length (or the parent's end) andSyntaxNodeBuilderrejects it. The four sites arescanWord(src/lexer.ts) andscanCasePatternEnd/scanTestToken/scanTestCloser(src/parser.ts).Proposed fix: clamp the skip to the scan bound,
idx = Math.min(idx + 2, end). The clamp is a no-op unless the backslash is the final character, so no other parse changes. I verified locally that the fulltree-sitter-bashsuite — including the differential comparison against the referencetree-sitter-bashcorpus — stays green, and added regression cases that fail before the change and pass after it.I'm happy to open the PR once this is approved.
Contribution