# Why std.Io.File.stat does not identify symbolic link here?

**URL:** <https://ziggit.dev/t/why-std-io-file-stat-does-not-identify-symbolic-link-here/17686>\
**Category:** Help\
**Tags:** standard-library\
**Created:** [September 22, 2026, 8:56pm UTC](https://ziggit.dev/t/why-std-io-file-stat-does-not-identify-symbolic-link-here/17686 "2026-09-22T20:56:43Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![vinymeuh](https://ziggit.dev/user_avatar/ziggit.dev/vinymeuh/32/6096_2.png) [@vinymeuh](https://ziggit.dev/u/vinymeuh)\
**Post date:** [September 22, 2026, 8:56pm UTC](https://ziggit.dev/t/why-std-io-file-stat-does-not-identify-symbolic-link-here/17686/1 "2026-09-22T20:56:43Z")

</div>

Hello

Am i missing something stupidly evident in the Zig version here ?

```zig
viny@nyx ~> zig version
0.17.0-dev.2131+d08989840
viny@nyx ~> ls --color=auto -l /home/viny/.gitconfig
lrwxrwxrwx 1 viny viny 27 27 avril 2025 /home/viny/.gitconfig -> dotfiles/home/dot-gitconfig
viny@nyx ~> ./zlink /home/viny/.gitconfig
/home/viny/.gitconfig is not a symbolic link
viny@nyx ~> ./clink /home/viny/.gitconfig
/home/viny/.gitconfig is a symbolic link

```

With **zlink.zig**

```zig
const std = @import("std");

pub fn main(init: std.process.Init) !void {
    const arena = init.arena.allocator();
    const io = init.io;

    var it = try init.minimal.args.iterateAllocator(arena);
    const program_name = it.next() orelse "zlink";

    const arg = it.next() orelse {
        std.debug.print("Usage: {s} <path>\n", .{program_name});
        std.process.exit(1);
    };

    const file = try std.Io.Dir.cwd().openFile(io, arg, .{ .mode = .read_only });
    const stat = try file.stat(io);
    if (stat.kind == .sym_link) {
        std.debug.print("{s} is a symbolic link\n", .{arg});
    } else {
        std.debug.print("{s} is not a symbolic link\n", .{arg});
    }
}

```

and **clink.c**

```zig
#include <stdio.h>
#include <sys/stat.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <path>\n", argv[0]);
        return 1;
    }

    struct stat st;

    if (lstat(argv[1], &st) != 0) {
        perror("lstat");
        return 1;
    }

    if (S_ISLNK(st.st_mode)) {
        printf("%s is a symbolic link\n", argv[1]);
    } else {
        printf("%s is not a symbolic link\n", argv[1]);
    }

    return 0;
}

```

---

<div class="post-metadata">

**Author:** ![spiffyk](https://ziggit.dev/user_avatar/ziggit.dev/spiffyk/32/7202_2.png) [@spiffyk](https://ziggit.dev/u/spiffyk)\
**Post date:** [September 22, 2026, 9:11pm UTC](https://ziggit.dev/t/why-std-io-file-stat-does-not-identify-symbolic-link-here/17686/2 "2026-09-22T21:11:08Z")

</div>

I’m not at the computer right now so I can’t check, but I’m pretty sure it’s because you’re `open`ing the symlink in the Zig version, causing the OS to dereference it. Thereby, you’re `stat`ing the target file instead of the link. (Setting `.follow_symlinks = false` in the open options might help.)

---

<div class="post-metadata">

**Author:** ![vinymeuh](https://ziggit.dev/user_avatar/ziggit.dev/vinymeuh/32/6096_2.png) [@vinymeuh](https://ziggit.dev/u/vinymeuh)\
**Post date:** [September 22, 2026, 9:25pm UTC](https://ziggit.dev/t/why-std-io-file-stat-does-not-identify-symbolic-link-here/17686/3 "2026-09-22T21:25:24Z")

</div>

Good catch, but unfortunately now it crash ☹

```zig
viny@nyx ~> ./zlink /home/viny/.gitconfig
error: SymLinkLoop
/home/viny/Lang/zig/zig-x86_64-linux-0.17.0-dev.2264+230c63650/lib/std/Io/Threaded.zig:4982:34: 0x119eec7 in dirOpenFilePosix (std.zig)
                        .LOOP => return error.SymLinkLoop,
                                 ^
/home/viny/Lang/zig/zig-x86_64-linux-0.17.0-dev.2264+230c63650/lib/std/Io/Dir.zig:581:5: 0x10374a0 in openFile (std.zig)
    return io.vtable.dirOpenFile(io.userdata, dir, sub_path, options);
    ^
/home/viny/Code/kondo/zlink.zig:15:18: 0x11e665e in main (zlink.zig)
    const file = try std.Io.Dir.cwd().openFile(io, arg, .{ .mode = .read_only, .follow_symlinks = false });

```

---

<div class="post-metadata">

**Author:** ![jdsmith](https://ziggit.dev/letter_avatar_proxy/v4/letter/j/f0a364/32.png) [@jdsmith](https://ziggit.dev/u/jdsmith)\
**Post date:** [September 22, 2026, 10:26pm UTC](https://ziggit.dev/t/why-std-io-file-stat-does-not-identify-symbolic-link-here/17686/4 "2026-09-22T22:26:32Z")

</div>

I’m not sure if this is the answer, but in the documentation of OpenFileOptions There is this:

```zig
path_only: bool = false

```

Indicates intent for only some operations to be performed on this opened file:

- `close`
- `stat` On Linux and FreeBSD, this corresponds to `std.posix.O.PATH`.

I’d try setting this option to true as well and see what happens.

---

<div class="post-metadata">

**Author:** ![xash](https://ziggit.dev/user_avatar/ziggit.dev/xash/32/5384_2.png) [@xash](https://ziggit.dev/u/xash)\
**Post date:** [September 23, 2026, 1:36am UTC](https://ziggit.dev/t/why-std-io-file-stat-does-not-identify-symbolic-link-here/17686/5 "2026-09-23T01:36:25Z")

</div>

You want `const stat = try std.Io.Dir.cwd().statFile(io, arg, .{ .follow_symlinks = false });`. In your zig version you were opening a file (following symlinks to even get a handle on one), and then checking the stats. So the equivalent in the C version would be to use `fstat`, not `lstat`.

---

<div class="post-metadata">

**Author:** ![vinymeuh](https://ziggit.dev/user_avatar/ziggit.dev/vinymeuh/32/6096_2.png) [@vinymeuh](https://ziggit.dev/u/vinymeuh)\
**Post date:** [September 23, 2026, 5:38pm UTC](https://ziggit.dev/t/why-std-io-file-stat-does-not-identify-symbolic-link-here/17686/6 "2026-09-23T17:38:37Z")

</div>

Thank you @xash it works !

```zig
const std = @import("std");

pub fn main(init: std.process.Init) !void {
    const arena = init.arena.allocator();
    const io = init.io;

    var it = try init.minimal.args.iterateAllocator(arena);
    const program_name = it.next() orelse "zlink";

    const arg = it.next() orelse {
        std.debug.print("Usage: {s} <path>\n", .{program_name});
        std.process.exit(1);
    };

    const stat = try std.Io.Dir.cwd().statFile(io, arg, .{ .follow_symlinks = false });
    if (stat.kind == .sym_link) {
        std.debug.print("{s} is a symbolic link\n", .{arg});
    } else {
        std.debug.print("{s} is not a symbolic link\n", .{arg});
    }
}

```
