Grep tool returns silent "no matches" for files containing a NUL byte (swallows ripgrep's "binary file matches" notice)

Resolved 💬 4 comments Opened Jun 7, 2026 by edimuj Closed Jul 14, 2026

Summary

If a text source file contains a raw NUL byte (0x00), the Grep tool returns a silent "no matches" — even when the pattern is present. ripgrep (the bundled backend) actually does find the match and prints a binary file matches (found "\0" byte ...) notice, but the Grep tool's output layer discards that line and surfaces nothing. The result is a correctness trap: an agent reads "no matches" as "this code does not exist in the codebase" and misdiagnoses accordingly.

This cost ~5 wasted tool calls in a real session before a manual rg in Bash revealed the binary-file notice that the Grep tool had hidden.

Environment

  • Backend: ripgrep bundled in the package — @anthropic-ai/claude-code/vendor/ripgrep/<arch>/rg (v14.1.1 here)
  • Platform: Linux x86_64 (also reproduces against host ripgrep 14.1.0 — behavior is identical, so this is not a binary-version issue)

Reproduction

# create a .ts file with a raw NUL and a clearly-present token
printf 'hello SIGNATURE_TOKEN here\x00more text\n' > nultest.ts

Then in Claude Code:

Grep: pattern="SIGNATURE_TOKEN", path="nultest.ts", output_mode="content"

Expected: the match, or at minimum a notice that the file was treated as binary.
Actual: "No matches found" — no match, no binary notice, no warning.

Why it happens (verified)

Running the bundled ripgrep directly shows it finds the match and writes a summary line to stdout (exit 0):

$ rg SIGNATURE_TOKEN nultest.ts
binary file matches (found "\0" byte around offset 26)        # <- stdout, exit 0

In content mode ripgrep replaces the match lines with that single binary file matches… line. The Grep tool parses stdout expecting structured path:line:text records; the binary file matches… line doesn't fit that shape, so it gets dropped and the user sees nothing.

Confirmed:

  • The notice is on stdout, not stderr, with exit 0.
  • rg -l (files-with-matches) prints the filename and rg -a/--text prints the real lines — so the information is available, the tool just isn't surfacing it.
  • Note: plain grep cannot reliably find NUL either (it treats NUL as a line separator), so the obvious double-check also misleads — rg is the only easy way to discover the cause.

Suggested fix (any one helps)

  1. Pass through the notice — when ripgrep emits binary file matches, surface it (e.g. nultest.ts: binary file — matched but not shown (contains NUL byte); re-run with text mode) instead of dropping the line.
  2. Auto-fall-back to --text when a binary/NUL file matches, and label the results as coming from a binary file.
  3. At minimum, distinguish "no matches" from "matched in a binary file, suppressed" in the tool's output so an agent doesn't read silence as absence.

The core problem is that silent empty-on-binary is indistinguishable from a genuine zero result, which directly causes wrong conclusions.

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗