# Idioms for error handling with contextual information?

**URL:** <https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118>\
**Category:** Help\
**Tags:** language\
**Created:** [July 22, 2025, 5:01am UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118 "2025-07-22T05:01:29Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![zmitchell](https://ziggit.dev/user_avatar/ziggit.dev/zmitchell/32/5377_2.png) [@zmitchell](https://ziggit.dev/u/zmitchell)\
**Post date:** [July 22, 2025, 5:01am UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/1 "2025-07-22T05:01:29Z")

</div>

I’m coming from the Rust ecosystem, where error handling is often in the form of a tagged union whose variants contain arbitrary payloads e.g.

```zig
enum Error {
    Foo { x: u32, y: u32 },
    Bar { msg: String }
}

```

As far as I can tell, in Zig errors are just values. This leads me to my question: what are the idioms/patterns for printing and handling errors with contextual information?

I suppose you could always return your own `FooResult` type that’s a `Result`-like tagged union, but then you lose the ability to automatically propagate the error variant with `try`.

For a concrete example, suppose you’re parsing input from a text file and want to return the column and line at which a parse error occurs.

---

<div class="post-metadata">

**Author:** ![npc1054657282](https://ziggit.dev/user_avatar/ziggit.dev/npc1054657282/32/5790_2.png) [@npc1054657282](https://ziggit.dev/u/npc1054657282)\
**Post date:** [July 22, 2025, 5:30am UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/2 "2025-07-22T05:30:56Z")

</div>

Add a parameter to the function signature for outputting error context.  
This is how I understand this setting: when the existence of a structure is bound to the error itself, there may be some memory problems in the construction of the structure itself. For example, if a new heap memory object needs to be constructed inside an error structure, then an error in the construction process is equivalent to a new error nested inside an error, which is confusing. If the creation of the error context is handed over to the programmer and the language is decoupled from the error code, then the construction error of the error context is grammatically thrown as an independent error, rather than an error in another error construction process, avoiding nested errors.  
In addition, some UAF problems may occur inside the construction process of this error structure. For rust, the language itself avoids this problem. But rust seems to have no measures for the generation of nested errors. It basically relies on the programmer’s self-consciousness in constructing the error structure to avoid nested errors.

---

<div class="post-metadata">

**Author:** ![squeek502](https://ziggit.dev/user_avatar/ziggit.dev/squeek502/32/409_2.png) [@squeek502](https://ziggit.dev/u/squeek502)\
**Post date:** [July 22, 2025, 5:33am UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/3 "2025-07-22T05:33:45Z")

</div>

Previously:

> [@Newbie: idiom to add information to error](https://ziggit.dev/t/newbie-idiom-to-add-information-to-error/10214/5):
>
> This is commonly referred to as the “Diagnostics” pattern. There are several examples in Zig’s stdlib. Here’s one for std.json: [std.json.Diagnostics](https://ziglang.org/documentation/master/std/#std.json.Diagnostics).

> [@Example using diagnostics for error handling with std.json](https://ziggit.dev/t/example-using-diagnostics-for-error-handling-with-std-json/7882):
>
> I can’t seem to find an example of using diagnostics to report error details. For example, I have this code that fails with a SyntaxError, but which line? const std = @import("std"); const Place = struct { lat: f32, long: f32 }; test "json parse" { const parsed = try std.json.parseFromSlice( Place, std.testing.allocator, \\{ \\ "lat": 40.684540, \\ "long": -74.401422, \\} , .{}, ); defer parsed.deinit(); const pla…

---

<div class="post-metadata">

**Author:** ![zmitchell](https://ziggit.dev/user_avatar/ziggit.dev/zmitchell/32/5377_2.png) [@zmitchell](https://ziggit.dev/u/zmitchell)\
**Post date:** [July 22, 2025, 5:43am UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/4 "2025-07-22T05:43:43Z")

</div>

Ah, thank you! All of my previous searches used the word “context” and I never thought to search for “diagnostics”.

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [July 22, 2025, 9:25am UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/5 "2025-07-22T09:25:23Z")

</div>

also just prefer to handle errors where you have the context to do so, rather than propagating them up

---

<div class="post-metadata">

**Author:** ![hvbargen](https://ziggit.dev/user_avatar/ziggit.dev/hvbargen/32/5696_2.png) [@hvbargen](https://ziggit.dev/u/hvbargen)\
**Post date:** [July 22, 2025, 3:16pm UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/6 "2025-07-22T15:16:39Z")

</div>

In my experience, 90% of all possible errors can only be handled reasonably by giving an information upwards that there was an error and clean-up resources along the way.  
For example, if you want to read text from a file, but the file doesn’t exist or you don’t have the permission to read or whatever, there is usually nothing the program can do to handle this, except informing the user and stop the processing, at least for this file.  
In languages with exception handling, as a rule of thumb, it’s best to just not catch each and every exception everywhere.  
Instead, there should be only a few exception handlers at a high level which inform the user and eg in case of DB applications, rollback the transaction.  
Zig has errors, not exceptions, but this is just a pure technical aspect and doesn’t change the concept.  
Back to the missing file example, the user needs to know where in the fs the program was looking for the file and what was the filename.  
It is very helpful if the program can add this information to an error structure.  
In its simplest form, this could be a string-to-string map.  
Regarding added complexity due to allocating, one could use a small arena allocator (say, 4kb) which is reserved only for error info (which should be global and cleaned up in certain places ). Adding error information should use this allocator and is allowed to fail silently (it’s just adding more info to the error).

---

<div class="post-metadata">

**Author:** ![zmitchell](https://ziggit.dev/user_avatar/ziggit.dev/zmitchell/32/5377_2.png) [@zmitchell](https://ziggit.dev/u/zmitchell)\
**Post date:** [July 22, 2025, 5:33pm UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/7 "2025-07-22T17:33:13Z")

</div>

The tricky part is where that location actually is. I suspect you already know that, but for anyone reading this in the future who wants more detail you can read the comment above mine.

Another example is when I’m developing a CLI, where we want consistent styling for errors regardless of where they’re generated in the application. Say an error occurs in a library I’m using. Even if the library has context for handling the error, it shouldn’t decide that part of “handling” the error is printing a message to the consumer since I’m the one that gets to make decisions about how that should look.

My original question is actually for a CLI I’m working on. I view it as having three layers and I was wondering how people would pass “diagnostics” from the bottom layer to the presentation layer:

- Presentation
- My business logic and implementation details
- Third-party code

---

<div class="post-metadata">

**Author:** ![pachde](https://ziggit.dev/user_avatar/ziggit.dev/pachde/32/2485_2.png) [@pachde](https://ziggit.dev/u/pachde)\
**Post date:** [July 22, 2025, 5:52pm UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/8 "2025-07-22T17:52:48Z")

</div>

It’s just another service that a library can provide.

Details are very much dependent on the particular service that a library might be providing.

For a language parser, or something else intrinsically hierarchical, it can easily mean the equivalent of a stack trace, which would lead you providing diagnostics as a list-like thing. Another pattern is a typed log that client code can traverse in reverse to glean the details it needs to present useful information to the user. For something quite flat, passing an optional allocator and simple out parameter can be a good solution.

---

<div class="post-metadata">

**Author:** ![hvbargen](https://ziggit.dev/user_avatar/ziggit.dev/hvbargen/32/5696_2.png) [@hvbargen](https://ziggit.dev/u/hvbargen)\
**Post date:** [July 22, 2025, 6:41pm UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/9 "2025-07-22T18:41:23Z")

</div>

The CLI using libraries is a good example.

One could say that the concept I suggested is a variation of the diagnostics pattern.  
Let’s call it “GD” for global diagnostics, and the pattern used in the standard library for JSON “LD” for local diagnostics.

LD uses specialized diagnostics for each type of possible error, with fields like eg line: u32.  
GD uses a generic structure: A string map or maybe string list.

In this regard, LD is more powerful if we actually want do _use_ this data anything else but showing it to the user or a log file. But if showing it is all we need, then the string map is probably more easy to use, since it is more generic.

With LD, as seen in the examples, usually the diagnostics struct is on the stack and has to be explicitly specified before used.  
This is in line with Zig’s philosophy, but at the same time a bit tedious.  
An advantage would be here that you don’t need to think about cleaning it up, unless you want to reuse it in the same function for a second call (even then you should check in the code that in case of an error _all_ fields are written to avoid leftovers from the previous error.

With GD, there would be only exactly one global diagnostics struct, using its own private memory.  
(I’m thinking of a single threaded program only).  
No need to pass it explicitly to each function.  
In case of an error, a function should either

- add to / adjust the diagnostics and return an error (not necessarily the same error) or
- handle/report the error (using the diagnostics) and then clear the diagnostics.

And just to be sure, also clear the diagnostics on a high level before starting the actual action.

Anyway, both patterns require that not only your own code, but also the libraries support them.  
Of course, if a library supports LD, you can use it also for GD (by formatting the diagnostic data fields to name/value strings and add them to the GD info).

As a side note, I’m coming from a background of languages with exceptions (eg. Python , Java) or quite restricted exceptions (PL/SQL), and while I’m fine with using errors instead of exceptions, the fact that it’s complicated to add useful info to errors is really a pain point.  
With errors I mean basically what Python class EnvironmentError means: something outside of the program’s control.  
Mere programming errors eg UB don’t need additional info, they are very well shown in the debug builds, I like that.

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [July 23, 2025, 12:39am UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/10 "2025-07-23T00:39:39Z")

</div>

@hvbargen @zmitchell sorry I should have been more descriptive.

Libraries should propagate errors, if not all context is available to the caller, the library should provide access to it.

> [@hvbargen](#):
>
> In languages with exception handling, as a rule of thumb, it’s best to just not catch each and every exception everywhere.  
> Instead, there should be only a few exception handlers at a high level which inform the user and eg in case of DB applications, rollback the transaction.  
> Zig has errors, not exceptions, but this is just a pure technical aspect and doesn’t change the concept.

yes, there will most likely be places where you can handle most errors while having the context to do so, my opinion is simply to avoid creating those places artificialy by propogating context as that means more code to maintain and debug.

> [@zmitchell](#):
>
> it shouldn’t decide that part of “handling” the error is printing a message to the consumer since I’m the one that gets to make decisions about how that should look

presenting the error to the user is a distinct part of error handling, I think its fine and common to seperate that part to do it seperately and more uniformly.

> [@hvbargen](#):
>
> Let’s call it “GD” for global diagnostics, and the pattern used in the standard library for JSON “LD” for local diagnostics.

My prefered way of this kind of diagnostics is for GD to also have the information that you would get from LD. That creates further separation from the creation and presentation of diagnostics, there is also an argument for using less memory, though that is less important as diagnostics usually isnt a hot path.

---

<div class="post-metadata">

**Author:** ![g41797](https://ziggit.dev/user_avatar/ziggit.dev/g41797/32/3339_2.png) [@g41797](https://ziggit.dev/u/g41797)\
**Post date:** [July 23, 2025, 4:25pm UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/11 "2025-07-23T16:25:51Z")

</div>

I tolerated c++/c# exception handling, error handling in go.  
I will tolerate zig also

---

<div class="post-metadata">

**Author:** ![npc1054657282](https://ziggit.dev/user_avatar/ziggit.dev/npc1054657282/32/5790_2.png) [@npc1054657282](https://ziggit.dev/u/npc1054657282)\
**Post date:** [July 24, 2025, 5:22am UTC](https://ziggit.dev/t/idioms-for-error-handling-with-contextual-information/11118/12 "2025-07-24T05:22:48Z")

</div>

What about this pattern?

> [@A derived global "Diagnostics" pattern](https://ziggit.dev/t/a-derived-global-diagnostics-pattern/11162):
>
> Recently, I’m learning the “Diagnostics” pattern through [std.tar](https://github.com/ziglang/zig/blob/0.14.0/lib/std/tar.zig#L28). I want to use Diagnostics as my structured error handling paradigm. To be honest, I am not fully satisfied with this implementation. I think it has the following problems: 1.Enabling or not enabling Diagnostics will cause changes in control flow other than error handling. When Diagnostics is enabled, some control flows that would have returned immediately due to an error will continue to execute. 2.After enabling Diagnostics, e…
