DEV Community

Mahiro Hirakawa
Mahiro Hirakawa

Posted on

The scanner read 2581 files and reported zero. The defect was on line 403.

On 2026-09-04 I pointed a scanner at langchain-ai/langchain. Shallow clone of the default branch, HEAD 79cab2d, read only. It walked 2581 files and printed zero sites. Its own control had passed immediately before the run, with two positive fixtures seen and four negative fixtures clean, so the zero was a measurement rather than a crash.

Then I opened one file by hand.

libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py, line 403:

def _should_interrupt(self, tool_call, config, state, runtime) -> bool:
    """Return False if the `when` predicate rejects this tool call, True otherwise."""
    when = config.get("when")
    if when is None:
        return True
    ...
    return when(req)
Enter fullscreen mode Exit fullscreen mode

when is supplied by the caller. It is declared NotRequired[Callable[[ToolCallRequest], bool]] on line 195 and documented as returning True to interrupt or False to auto-approve. Its result is handed back unchanged. A predicate that falls off a branch returns None, and the caller on line 436 reads:

if not self._should_interrupt(tool_call, config, state, runtime):
    continue
Enter fullscreen mode Exit fullscreen mode

None is falsy. The interrupt is skipped and the tool call proceeds with nobody looking at it. The annotation says bool; nothing at runtime makes that true.

Why the machine stayed quiet

I took the failure apart instead of guessing at it. Three causes, each sufficient on its own:

Vocabulary. 22 lines in that file matched the approval vocabulary the scanner looks for. Not one of them put line 403 inside its window. The nearest match was 26 lines away and sat in a comment. This project calls the decision interrupt, not approval.

Window. The -> bool annotation is on line 378. The return is on 403. That is 25 lines apart, and the window was 12.

Signals. Widened to 55 lines, the three behaviour signals still matched nothing on that line.

The file walk was innocent. The file is .py, 18256 bytes, and no skip rule matched it. It was read.

What I got wrong

The window of 12 lines had no measurement behind it. I picked a number that felt right and it was smaller than the distance between a signature and its return in ordinary formatted Python.

Worse, and earlier: this same tool shipped with fixtures in its skip list. The self-test therefore scanned zero files, all four negative fixtures came back clean, and the control printed a pass. A tool built to catch an empty scan being read as a success shipped with an empty scan being read as a success.

Then, fixing the first problem, I introduced a third. I decided where a function ended with a line-end anchor. Every clone on this machine is CRLF, so a carriage return sat between the colon and the anchor and it never matched once. Signatures became fixed blocks that spanned several definitions, and rows got attributed to the wrong function. I found that by reading three surprising rows, not by any check.

What I did not check

One repository, one commit. I make no claim about other versions or other branches.

Whether a when predicate returning None exists in any real deployment: not checked. This is reachability, not a demonstrated exploit.

The two signals I added to catch it raise detections on repositories that implement this correctly by exactly one line across four of them, in agno at os/auth.py:231. Reading it shows working code. I left the number where it landed instead of tuning it away.

Cost: on agno, 4806 files, nine alternating runs put the median at 930ms before and 1085ms after, up 17%. On open-interpreter the difference was smaller than the run-to-run spread.

The part worth keeping is not the fix. It is that a completed scan returning zero is a statement about the scanner, and reading the file afterwards is still the job.

Trace: ledger bands/decisions/01_RULINGS.md entries D-G013, D-G014 and D-G015; machine record offer/scans/langchain.json.

Repository: docs/LIMITS.md is where the same project writes down what its own checks do not cover.

Runnable reproductions for every framework named above, offline and pinned to a version: https://github.com/mahirhir/unanswered-approval

Top comments (0)