Baobaobaolin.com
date
entry
053
topic
tooling
rev
—

In Claude Code on macOS, find is bfs: -newermt "-5 minutes" returns Invalid timestamp, and wc -l counts 0

Around 1:10 a.m. on October 18 (the example time printed in bfs's error message was 01:10:56), I wanted to count files modified in the last five minutes in Go's build cache, ran find … -newermt "-5 minutes" | wc -l, and got 0. Checking the cache later, it held a file with a modification time of 01:06:07. The find in Claude Code is actually the embedded bfs, which doesn't accept that kind of relative time; the error went to stderr, wc received an empty stdout, and counted 0.

To find recently modified files on macOS, I usually write find dir -newermt "-5 minutes". macOS's /usr/bin/find has its own date parser that accepts "minus five minutes," "yesterday" and the like, and it had always worked.

But in the early hours of October 18, the same command in Claude Code's Bash tool, piped into | wc -l, gave 0, with a few lines of bfs: error above it. 042 covered how grep in this environment is the embedded ugrep and mentioned that find is likewise the embedded bfs, but its tests centered on grep and didn't cover find. This one fills in find: what it looks like, four ways it differs from macOS find, when the system find runs instead, and how I write it now. Everything below is from Claude Code 2.1.302, the native macOS build, in a zsh session.

find is a function

type find says it's a function, and which find (zsh prints the function body) shows the contents, excerpted:

$ type find
find is a shell function from /Users/…/.claude/shell-snapshots/snapshot-zsh-….sh
$ which find
find () {
	…
	if [[ -n ${ZSH_VERSION:-} ]]
	then
		ARGV0=bfs "$_cc_bin" -S dfs -regextype findutils-default ${1+"$@"}
	…
}

$_cc_bin is Claude Code's own executable, started under the name bfs, with -S dfs -regextype findutils-default always prepended. Asking for the version the same way, ARGV0=bfs claude --version returns bfs 4.1.1. Claude Code's changelog for 2.1.117 and the tools documentation (the Glob section of the Tools reference) both say so: in native macOS and Linux builds, find and grep run the embedded bfs and ugrep.

Difference one: -newermt time formats

In a scratch directory I ran six time formats against the same directory and recorded the exit codes:

format               bfs   /usr/bin/find
"-5 minutes"          1       0
"5 minutes ago"       1       0
"yesterday"           1       0
"2026-10-17"          0       0
"2026-10-17 10:00"    0       0
"@1792200000"         0       1

When bfs rejects one, the error is clear and even lists the formats it accepts (excerpt; the example times follow the clock):

bfs: error: Invalid timestamp.

Supported timestamp formats are ISO 8601-like, e.g.

  - 2026-10-18
  - 2026-10-18T01:10:56
  - 2026-10-18T01:10:56+08:00
  - 2026-10-17T17:10:56Z

The trouble is that this goes to stderr. The pipe only hands stdout to wc; stdout was empty, so wc honestly counted 0. The error still prints on screen, but if you only read the final number, the whole command looks like "no matching files." Put 2>/dev/null on the find side (find … 2>/dev/null | wc -l) and you don't even see the error. The bfs 4.1.1 manual says the reference for -newerXt is an "ISO 8601-style timestamp," and none of the three relative times tested here is accepted; conversely, @ plus Unix seconds is accepted only by bfs, not macOS find. Of these six, the only formats both accept are the two absolute dates and times.

Difference two: -regex is GNU-style

The function always adds -regextype findutils-default. macOS find defaults to POSIX basic regular expressions, where + and \| are ordinary characters. On the same test tree:

-regex '.*/[0-9]+'          bfs: ./12 ./a/1 ./c/3 ./a/b/2   /usr/bin/find: (none)
-regex '.*/\(a\|c\)'        bfs: ./a ./c ./a/b/c            /usr/bin/find: (none)
find -E . -regex '.*/(a|c)' /usr/bin/find: ./a ./a/b/c ./c

The same pattern finds 4 files in Claude Code and 0 in the terminal, and neither side reports an error. As for findutils-default: the bfs 4.1.1 manual page (docs/bfs.1) doesn't list it among -regextype values, but -regextype help does, described as "Like GNU find." The results agree: + and \| are operators.

Difference three: -printf and output order

-printf is a GNU find action that macOS find lacks. find . -name z -printf '%s %p\n' prints 0 ./z in Claude Code and find: -printf: unknown primary or operator in the terminal. A command written and tested in Claude Code can break once pasted into a deploy script.

Output order differs too. Same test tree:

bfs:           . ./1a ./z ./a ./c ./12 ./a/1 ./a/b ./c/3 ./a/b/c ./a/b/2 ./a/b/c/d ./a/b/c/d/x
/usr/bin/find: . ./1a ./z ./a ./a/1 ./a/b ./a/b/c ./a/b/c/d ./a/b/c/d/x ./a/b/2 ./c ./c/3 ./12

On this tree, bfs lists a whole level before going deeper; macOS find finishes each directory as it meets it. Oddly, the function does add -S dfs (depth-first, per the manual), and when I also ran -S bfs and -S dfs -j1, all three outputs were identical. I haven't found why -S dfs doesn't change the output order. The practical effect: anything relying on output order can get different items on each side. For example, find . -type f | head -3 on this tree gives ./1a ./z ./12 from bfs and ./1a ./z ./a/1 from macOS find. Neither order is sorted; if you need a stable one, sort it yourself.

When the system find runs instead

As with grep in 042, find is a shell function and only applies when called from a shell that holds the definition. Wrappers that "go run another program," like timeout and xargs, look up the executable on PATH; with this session's PATH, that's /usr/bin/find:

$ timeout 5 find . -maxdepth 0 -newermt "-5 minutes"; echo rc=$?
.
rc=0
$ echo . | xargs -I{} find {} -maxdepth 0 -newermt "-5 minutes"; echo rc=$?
.
rc=0

(The . prints because the directory had been modified within the five minutes.) The same command with timeout 5 in front goes from error to success. So the behavior you see in Claude Code isn't necessarily the behavior your script will have.

How I write it now

For commands that must behave the same in this Mac's terminal, deploy scripts and Claude Code, I write /usr/bin/find. command find also skips the function, but it takes the first find on PATH, which may differ on a machine with GNU find installed. Time conditions use absolute times both sides accept, computed first and passed in: find dir -newermt "$(date -v-5M '+%Y-%m-%d %H:%M:%S')" (that's macOS date syntax). When I only need to match file names, I use -name instead of -regex (-name matches a glob against the base name; -regex matches the whole path); when I really need -regex, I run it on both sides and compare.

Back to that 0: had I not noticed the error lines above it, I might have concluded "nothing new in the cache in the last five minutes." For any find feeding a pipeline, I now run it alone once and check the exit code, or turn on pipefail so a failure early in the pipeline shows up in the whole command's exit code.

If you remember one thing

In the Bash tool of Claude Code 2.1.302's native macOS build, find is the embedded bfs: -newermt rejects the relative times tested here, -regex is GNU-style, -printf works, and output order differs; errors go to stderr, so reading only the wc -l number makes it look like nothing matched. To match this machine's terminal and scripts, write /usr/bin/find.

Revision history

  1. First published