# Timeout example with std.Io

**URL:** <https://ziggit.dev/t/timeout-example-with-std-io/16818>\
**Category:** Brainstorming\
**Created:** [July 21, 2026, 5:02am UTC](https://ziggit.dev/t/timeout-example-with-std-io/16818 "2026-07-21T05:02:14Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![lipfang](https://ziggit.dev/user_avatar/ziggit.dev/lipfang/32/9809_2.png) [@lipfang](https://ziggit.dev/u/lipfang)\
**Post date:** [July 21, 2026, 5:02am UTC](https://ziggit.dev/t/timeout-example-with-std-io/16818/1 "2026-07-21T05:02:15Z")

</div>

It sounds like we’re getting `concurrentTimeout` in a future release, but meanwhile, this is what I’ve done to get timeouts with `std.Io` leveraging `std.Io.Select`. Let me know if I can improve this in any way! After all, this is a _lot_ of boilerplate to accomplish a simple timeout.

```zig
    var attachments_arena: std.heap.ArenaAllocator = .init(gpa);
    defer attachments_arena.deinit();

    const SelectT = union(enum) {
        attachments: []const []const u8,
        timeout: void,
    };
    var buf: [2]SelectT = undefined;
    var select: std.Io.Select(SelectT) = .init(io, &buf);
    defer select.cancelDiscard();

    try select.concurrent(.attachments, downloadAttachments, .{ gpa, attachments_arena.allocator(), io, urls });
    try select.concurrent(.timeout, std.Io.sleep, .{ io, .fromSeconds(10), .awake });

    const files: []const []const u8 = switch (try select.await()) {
        .attachments => |attachments| attachments,
        .timeout => if (select.cancel()) |awaited| switch (awaited) {
            .attachments => |attachments| attachments,
            else => &.{},
        } else &.{},
    };

```

Also some additional feedback with `concurrentTimeout` (at least based on the [proposal](https://codeberg.org/ziglang/zig/issues/31098)):

Adding another version of `error.Canceled` (`error.Timeout`) is a bit… annoying. Specifically, there isn’t much meaningful distinction to me, and as a library author, this now means propagating 2 errors everywhere I used to only propagate 1. This kind of propagation is especially annoying in cases where the error isn’t readily available, and I need to grab it from a reader’s state machine… although I guess now that I’ve already done the work, maybe it won’t be too bad. I know Zig has breaking changes nearly every release, but adding a new error to `std.Io.Cancelable` does not feel “worth it”. In my opinion, a timeout is just a different flavor of a Cancel, and they should just return `error.Canceled`, as library authors have already built infrastructure specifically for handling that error.

Another thing that could help library authors is the idea of a way to match if an error is part of an error set. Needing to `switch (err) { error.Canceled, error.Timeout => |err| return err, ... }` is going to get needlessly verbose and entirely defeats the purpose of Zig having great error handling. Something like `switch (err) { std.Io.Cancelable => |err| return err, ... }` gives library authors a single identifier they can use to say “these are the errors that should be propagated”. It’d also be extremely useful outside of this more specific case, being able to tell if an I/O error was caused by, say, a JSON parse error vs a network stream error.

---

<div class="post-metadata">

**Author:** ![pasta](https://ziggit.dev/user_avatar/ziggit.dev/pasta/32/6273_2.png) [@pasta](https://ziggit.dev/u/pasta)\
**Post date:** [July 21, 2026, 10:55am UTC](https://ziggit.dev/t/timeout-example-with-std-io/16818/3 "2026-07-21T10:55:08Z")

</div>

I’ve had similar, in general cancelling concurrent tasks in a select/group can be a little bit of a tricky challenge.

The only thing I can offer is a slight “simplification” on the result handling code.

> [@lipfang](#):
>
> ```zig
> const files: []const []const u8 = switch (try select.await()) {
> .attachments => |attachments| attachments,
> .timeout => if (select.cancel()) |awaited| switch (awaited) {
> .attachments => |attachments| attachments,
> else => &.{},
> } else &.{},
> };
> 
> ```

I believe this should work in your case? I use similar [here](https://codeberg.org/mdiluz/flyt/src/commit/2352e775b1f7562132986d67fbb67c614469b790/src/daemon/Context.zig#L474):

```rust
var next: ?SelectT = select.await() catch null;
while (next) |result| {
    switch (result) {
       .attachments => |attachments| return attachments,
       else => {},
    }
    next = select.cancel();
}
return &.{};

```

edit: @xeondev has a much better suggestion for your use case, in mine I’m having to deal with two concurrents, but yours is just one.

On the other points:

> [@lipfang](#):
>
> Adding another version of `error.Canceled` (`error.Timeout`) is a bit… annoying. Specifically, there isn’t much meaningful distinction to me

Agree on this, I’ll probably put some feedback on the proposal too.

> [@lipfang](#):
>
> Another thing that could help library authors is the idea of a way to match if an error is part of an error set

On this, I’m unsure I agree - an `else` error handle is already _somewhat_ of a code smell as more errors could be added to the return type and you will not know. Given that errors should be control flow indicators. An error set switch prong would have the same problem, but _look_ less like a code smell.

---

<div class="post-metadata">

**Author:** ![xeondev](https://ziggit.dev/user_avatar/ziggit.dev/xeondev/32/9912_2.png) [@xeondev](https://ziggit.dev/u/xeondev)\
**Post date:** [July 21, 2026, 10:57am UTC](https://ziggit.dev/t/timeout-example-with-std-io/16818/4 "2026-07-21T10:57:36Z")

</div>

Alternatively, you can use something like `std.Io.Event`, pass it to the `concurrent` task and call `waitTimeout` in the caller task. The callee is expected to call `std.Io.Event.set` on exit. This saves you one `concurrent` task (as you won’t have a `concurrent` sleeper)

---

<div class="post-metadata">

**Author:** ![alanza](https://ziggit.dev/user_avatar/ziggit.dev/alanza/32/6006_2.png) [@alanza](https://ziggit.dev/u/alanza)\
**Post date:** [July 21, 2026, 6:52pm UTC](https://ziggit.dev/t/timeout-example-with-std-io/16818/5 "2026-07-21T18:52:15Z")

</div>

this sounds like a great solution to me, thanks for pointing it out.

---

<div class="post-metadata">

**Author:** ![dee0xeed](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/3ab097/32.png) [@dee0xeed](https://ziggit.dev/u/dee0xeed)\
**Post date:** [August 8, 2026, 4:54pm UTC](https://ziggit.dev/t/timeout-example-with-std-io/16818/6 "2026-08-08T16:54:44Z")

</div>

check out this

> **[GitHub - dee0xeed/edsm-in-zig-demo-2: simple EDSM-based server and client](https://github.com/dee0xeed/edsm-in-zig-demo-2)**
>
> simple EDSM-based server and client

`request timeout (note T0)`  
very simple and no need for some special library support
