std.Io.Dir.readFileAlloc() returns 0 bytes when reading /proc files?

This seems silly, I must be overlooking something obvious.

I’m trying to read the entire contents of /proc/version into a buffer. Saw std.Io.Dir.readFileAlloc() looked simple enough, but I keep getting 0 bytes back. No error, just an empty buffer. If I attempt the same thing with a more verbose std.Io.Dir.readSliceShort(), it works just fine. Here’s two test cases.

test "WORKS - read /proc/version with readSliceShort()" {
    const proc_dir = try Io.Dir.openDirAbsolute(testing.io, "/proc", .{ .iterate = false });
    defer proc_dir.close(testing.io);

    const proc_version_file = try proc_dir.openFile(testing.io, "version", .{
        .mode = .read_only,
        .allow_directory = false,
    });
    defer proc_version_file.close(testing.io);

    var file_reader = proc_version_file.reader(testing.io, &.{});
    var intf = &file_reader.interface;

    var file_buf: [1024]u8 = undefined;
    const len = try intf.readSliceShort(
        &file_buf,
    );

    const file_contents = file_buf[0..len];

    print("{d} bytes\n", .{file_contents.len});
    print("\"{s}\"\n\n", .{file_contents});
    try testing.expect(file_contents.len > 0);
}

test "FAILS - read /proc/version with readFileAlloc()" {
    const proc_dir = try Io.Dir.openDirAbsolute(testing.io, "/proc", .{ .iterate = false });
    defer proc_dir.close(testing.io);

    const file_contents = try proc_dir.readFileAlloc(
        testing.io,
        "version",
        testing.allocator,
        .limited(4096),
    );
    defer testing.allocator.free(file_contents);

    print("{d} bytes\n", .{file_contents.len});
    print("\"{s}\"\n\n", .{file_contents});
    try testing.expect(file_contents.len > 0);
}

const Io = @import("std").Io;
const print = @import("std").debug.print;
const testing = @import("std").testing;

On my machine I get the following output:

$ zig version
0.16.0

$ zig test ./src/read_proc_version.zig
213 bytes
"Linux version 6.12.100+deb13-amd64 (debian-kernel@lists.debian.org) (x86_64-linux-gnu-gcc-14 (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Debian) 2.44) #1 SMP PREEMPT_DYNAMIC Debian 6.12.100-1 (2026-07-30)
"

0 bytes
""

2/2 read_proc_version.test.FAILS - read /proc/version with readFileAlloc()...FAIL (TestUnexpectedResult)
/home/alex/opt/zig-x86_64-linux-0.16.0/lib/std/testing.zig:615:14: 0x12388f9 in expect (std.zig)
    if (!ok) return error.TestUnexpectedResult;
             ^
/home/alex/devel/zigko/src/read_proc_version.zig:40:5: 0x1238bb3 in test.FAILS - read /proc/version with readFileAlloc() (read_proc_version.zig)
    try testing.expect(file_contents.len > 0);
    ^
1 passed; 0 skipped; 1 failed.
error: the following test command failed with exit code 1:
.zig-cache/o/4bb3371dc7a3aacb6b73029e5f20a14f/test --seed=0xe1c60156

According to this other answer, it sounded like readFileAlloc() does not check the file’s size first, so I didn’t think /proc/... being a virtual file system would be a problem for readFileAlloc().

The reason is simple, readFileAlloc() does not querry the file size when reading.

Need help understand readfileAlloc() - #3 by Bobvan

General suggestions for investigating this type of stuff (in no particular order): (a) use strace to see which syscalls are being called and what they return, and then you can compare the strace logs to see what’s different between the two pieces of code, (b) read the relevant standard library code

My attempt at (b) shows that readFileAlloc eventually will end up at std.Io.Writer.Allocating.sendFile which has this logic so if File.Reader.getSize() is returning 0 for /proc/version then that explains what you’re seeing. readSliceShort instead uses Reader.readVec which doesn’t have that logic.

(not commenting on how this should work, this is just my understanding of how it works currently)

3 Likes

If you scroll down to my comment in the same thread, you’ll see that it does actually query the size of the file, as @squeek502 explained more succinctly.

1 Like

My bad. I stopped reading the thread a few replies after the marked solution. Thanks!

Speaking of reading more, I just found an open issue on this topic. Happy to know other folks are aware of this.

https://codeberg.org/ziglang/zig/issues/31946

Thanks for the tips. I did read some of the standard library code. I believe I got as far down as std.Io.Reader.allocRemainingAlignedSentinel() or std.Io.Reader.appendRemainingAligned() and saw no signs of reading a file size.

Looking at this again with a fresh cup of coffee, I suppose I should have realized the std.Io is generic, file-specifics would be in std.Io.File

To prove this difference between readFileAlloc() and readSliceShort() I commented out the implementation of File.Reader.getSize() with a panic and sure enough only readFileAlloc() dies.

Taking your (a) advise, seems like this should be the relevant strace output for the readFileAlloc() test case:

openat(AT_FDCWD, "/proc", O_RDONLY|O_CLOEXEC|O_PATH|O_DIRECTORY) = 3
openat(3, "version", O_RDONLY|O_NOCTTY|O_CLOEXEC) = 4
statx(4, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_TYPE|STATX_MODE|STATX_NLINK|STATX_ATIME|STATX_MTIME|STATX_CTIME|STATX_INO|STATX_SIZE|STATX_BLOCKS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0444, stx_size=0, ...}) = 0
close(4)                                = 0
gettid()                                = 451368
ioctl(2, TIOCGWINSZ, {ws_row=48, ws_col=140, ws_xpixel=2676, ws_ypixel=1838}) = 0
writev(2, [{iov_base="0 bytes\n", iov_len=8}], 10 bytes
) = 8
writev(2, [{iov_base="\"\"\n\n", iov_len=4}], 1""

) = 4
close(3)                                = 0

If I’m understanding correctly stx_size=0 is the part saying the file size is zero. This matches what I see from stat.

$ stat  /proc/version
  File: /proc/version
  Size: 0               Blocks: 0          IO Block: 1024   regular empty file
Device: 0,23    Inode: 4026532024  Links: 1
Access: (0444/-r--r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2026-08-13 21:26:14.275999932 -0700
Modify: 2026-08-13 21:26:14.275999932 -0700
Change: 2026-08-13 21:26:14.275999932 -0700
 Birth: -

Thanks all, I appreciate the help learning something today!

1 Like