Hi everybody I’m trying to take advantage of the --watch command line option for zig build, but unfortunately I hit a roadblock. I thought I would open a thread here and see if I can get some quick help before possibly opening an Issue on GitHub.
The issue is the following: whenever I try to run the following command:
zig build --watch
At some point I get the following in the output:
Build Summary: 3/3 steps succeeded
error: OperationNotSupported
/usr/lib/zig/std/posix.zig:7452:23: 0x14a72a0 in name_to_handle_atZ (build)
.OPNOTSUPP => return error.OperationNotSupported,
^
/usr/lib/zig/std/posix.zig:7436:5: 0x1480f20 in name_to_handle_at (build)
return name_to_handle_atZ(dirfd, &pathname_c, handle, mount_id, flags);
^
/usr/lib/zig/std/Build/Watch.zig:135:13: 0x1480b7b in getDirHandle (build)
try posix.name_to_handle_at(path.root_dir.handle.fd, adjusted_path, stack_ptr, &mount_id, std.os.linux.AT.HANDLE_FID);
^
/usr/lib/zig/std/Build/Watch.zig:195:48: 0x14814bf in update (build)
const dir_handle = try Os.getDirHandle(gpa, path);
^
/usr/lib/zig/std/Build/Watch.zig:862:5: 0x14824e5 in update (build)
return Os.update(w, gpa, steps);
^
/usr/lib/zig/compiler/build_runner.zig:460:9: 0x1488abe in main (build)
try w.update(gpa, run.step_stack.keys());
^
error: the following build command failed with exit code 1:
/home/andrea/Projects/CodeCrafters/codecrafters-redis-zig/.zig-cache/o/d92c54bbf23f33a010ef5d1a5f396163/build /usr/bin/zig /usr/lib/zig /home/andrea/Projects/CodeCrafters/codecrafters-redis-zig /home/andrea/Projects/CodeCrafters/codecrafters-redis-zig/.zig-cache /home/andrea/.cache/zig --seed 0x2bfe50da -Z15d74ee03ef52efc --watch
I did a little bit of research on the incriminated syscall (i.e. name_to_handle_at), and, as far as I can tell, all the kernel build config options and the filesystem I’m on (BTRFS) should provide support for it. I’m wondering which path is being given as an argument to the syscall, maybe that would point me closer to the issue. Sadly, this information is not available in the output above. I even tried to build Zig myself in the hope of doing some troubleshooting, but that didn’t go well either (failed on multiple linking errors to C++ libraries, like undefined symbol: std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char>>::basic_stringstream())
Does anybody have a clue what might be going on here, or what I could do to investigate further?
Here is a summary of my setup:
Zig version: 0.14.1
Operating System: Linux
uname -a: Linux fedora 6.15.9-201.fc42.x86_64 #1 SMP PREEMPT_DYNAMIC Sat Aug 2 11:37:34 UTC 2025 x86_64 GNU/Linux
Linux distribution: Fedora Linux 42 (Workstation Edition)
You’re thinking of ZIG_BTRFS_WORKAROUND=1 which worked around btrfs bug of silently failing rewinding a readdir operation.
This looks like a different issue, in which btrfs does not support a fundamental operation that Zig’s watch system depends on: name_to_handle_at. Have the btrfs folks made any statement about this?
I think you might have lead me to the issue: the zig binary I’m running is not on my host system, it’s installed in a distrobox container (basically a Podman container running Arch Linux with very little isolation w.r.t. the host system). That’s because the version of Zig that came packaged with Fedora was still 0.13 a while ago, and I needed to work with 0.14, which was instead the packaged version on Arch Linux at the time. Now, the /usr/lib/zig mentioned above is not on a BTRFS subvolume, it’s a overlayfs mount:
df -T /usr/lib/zig
Filesystem Type 1K-blocks Used Available Use% Mounted on
overlay overlay 941730816 90120076 848728084 10% /
And I think I’ve read around that overlayfs does not support name_to_handle. Looks like mystery solved, except that now I have to find a way to make /usr/lib/zig not a overlayfs mount (maybe make it a bind mount to a directory on my host machine? What a headache ).
Thank you so much everyone for looking into this, and thank you @Cloudef for helping it find the answer
More of a self-imposed “hygiene” discipline than anything else really. I usually don’t like to install software that is not packaged or kept track of in some way (e.g. like rustup does for Rust toolchains), so I tend not to download static binaries and put them under /opt or something for example. I used to make my own packages in some cases when I was back on Arch, since it was dirt simple, but I fear that with Fedora packaging would a little more trouble.
Still, a good idea to consider. Thanks for pointing it out
I do the same and my reasoning is that I like to have different home directories for different languages (and certain projects).
For example I have a custom home directory for:
C++
Rust
KDE projects (if you ever built them, you probably know why)
Zig
Go
and more
This also has the advantage of allowing me to have different configuration for these environments (e.g. different environment variables, config files etc.).
Maybe having fully packaged software is better when you have more of a “sprawl” of files, some of which need to be in standard directories (e.g. XDG files), and then it’s nice to have them all being part of the same package, in case I wonder where a file comes from or whether I should touch it or not. Or if you download updates every other week, then it’s handy if it’s all managed by the package manager.
But in the case of Zig, it’s probably just a binary (or more) and the zig source code, which are not too bad and can be in a dedicated directory. Plus I almost never need to update Zig. I also found out I can configure ZLS to work with custom installation paths pretty easily looking at the docs, so I’m really making it harder for myself if I insist on using my arch distrobox container just so that Zig is packaged
Thanks guys, I think I’ve had a small epiphany today thanks to the conversation in this thread