<p>Most languages have a system where exceptions are classes and you can <code>throw</code> them then use a <code>try/catch</code> block to handle them.</p>
<p>What alternate runtime error handling systems are used, and what are their benefits and drawbacks?</p>

<language-comparison><error-handling>
What are different ways of handling runtime errors?
CC BY-SA 4.0
2023-05-20T17:28:20.300Z
For those who see this, it's best to allow the original author of an example question to post it; this was a slight misunderstanding, and I give my blessing to mousetail to post it.
Seggan
<h1>Aborts/Panics</h1>
<p>This is the system Rust uses: There's a &quot;result&quot; type (<code>Result&lt;T&gt;</code>) for recoverable errors, like a malformed URL in a HTTP library, and an &quot;abort&quot; function/macro (<code>panic!</code>) for unrecoverable errors like indexing out of an array. Recoverable errors are returned from functions and can be processed like any other type, but unrecoverable errors crash the program instantly.</p>

CC BY-SA 4.0
2023-05-16T17:33:47.133Z
2023-05-16T17:35:20.900Z
2023-05-16T17:35:20.900Z
Ginger
CC BY-SA 4.0
2023-05-20T16:39:26.857Z
As mousetail said, there's stuff like [`catch_unwind`](https://doc.rust-lang.org/std/panic/fn.catch_unwind.html) (doesn't catch all panics, though)
user
CC BY-SA 4.0
2023-05-30T21:48:12.110Z
Lua also has sort of panics. `error(msg)` and `pcall(...)`
Hydroper
CC BY-SA 4.0
2023-05-16T17:35:43.793Z
That's not true though, you can write a panic handler in rust
mousetail 'he-him'
<h1>Out-params</h1>
<p>In Objective-C, idiomatic error handling uses an extra parameter:</p>
<pre class="lang-objectivec prettyprint-override"><code>error:(NSError * _Nullable *)error;
</code></pre>
<p>This allows you to suppress errors by passing <code>NULL</code> for that parameter, but also requires a bit more setup for both the caller (have to declare <code>NSError *error;</code> and then pass <code>&amp;error</code>) and the callee (have to check <code>if (error != NULL)</code> before writing to it).</p>

CC BY-SA 4.0
2023-05-16T19:21:06.327Z
2023-05-18T21:56:43.363Z
2023-05-18T21:56:43.363Z
Bbrk24
<h1>In C</h1>
<h2><code>errno</code></h2>
<p>Many functions in C such as <code>malloc()</code> or I/O functions such as <code>fwrite()</code> will return an error status such as <code>NULL</code> or <code>-1</code> on error and set the global error state <code>errno</code> to a error code describing the error.</p>
<h2><code>setjmp()</code>/<code>longjmp()</code></h2>
<p>A common way to implement exception handling in C is with <code>setjmp()</code> and <code>longjmp()</code>. Here is an example:</p>
<pre><code>#include &lt;setjmp.h&gt;
jmp_buf buf;
void throws() {
    // ...
    if (error_occurred) {
        longjmp(buf, -1);
    }
}
int main() {
    int err = setjmp(buf);
    if (err) { // If this point was reached by the longjmp() call
        handle_error(err);
        return;
    }
    // ...
}
</code></pre>

CC BY-SA 4.0
2023-05-17T00:03:24.343Z
2023-05-17T00:03:24.343Z
CPlus
CC BY-SA 4.0
2023-07-03T14:23:04.127Z
Your use of `setjmp` there actually isn't permissible under the standard. You can only use it in a control-flow conditional (`if (setjmp())`/`switch (setjmp())`/`while (setjmp())`), apply it directly to a comparison or boolean operator (`!setjmp()`/`setjmp() > 0`), or discard the result (`(void)setjmp();`).
Bbrk24
CC BY-SA 4.0
2023-07-14T12:15:40.470Z
Technically, [`errno`](https://en.cppreference.com/w/c/error/errno) is defined as a macro, and thus it's like it's in the global namespace, but its value is thread-local.
G. Sliepen
CC BY-SA 4.0
2024-12-23T00:28:16.223Z
@Bbrk24: The Standard fails to require that implementations support the construct, but there's no sign that the authors intended that implementations not support it if practical, and I am unaware of any implementations that can't handle that use case.
supercat
<ul>
<li>Monadic errors, like <code>Result&lt;T&gt;</code> in Rust. You need to manually catch the error in order to get the result.</li>
<li>Exceptions, like in Kotlin/Scala/Python/C#. By default, you use the return value, and you can optionally catch the error encountered.</li>
<li>Checked exceptions. Like exceptions, but statically check which exceptions are propagated and which are caught.</li>
<li>Row-polymorphic checked exceptions. Like checked exceptions, but use row polymorphism to allow higher-order checked exceptions to &quot;flow-through&quot; higher-order functions. <a href="https://www.microsoft.com/en-us/research/video/records-sums-cases-and-exceptions-row-polymorphism-at-work/" rel="noreferrer">Video</a> and <a href="https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.210.2810&amp;rep=rep1&amp;type=pdf" rel="noreferrer">compiler description</a> (see page 34)
<ul>
<li>Checked exceptions can be seen as first-order version of row-polymorphic exceptions</li>
</ul>
</li>
<li>C-style error handling. Return value of a function is error code, and the output of the function is stored in a pointer allocated by the caller. This is a very good approach for languages that needs programmers to manage memory, because we don't have to worry about deallocation of return values as library authors.</li>
</ul>
<p>You could also have no error handling, and abort the process if any runtime error is encountered.</p>
<p>A short note on checked exceptions: you may use generics to have higher order functions like this:</p>
<pre><code>@FunctionalInterface
interface Fn&lt;T, V, E extends Throwable&gt; {
  V apply(T t) throws E;
}

public static &lt;T, V, E extends Throwable&gt; V modusPonens(Fn&lt;T, V, E&gt; f, T t) throws E {
  return f.apply(t);
}
</code></pre>
<p>So that the exception thrown for <code>modusPonens</code> depends on the exception thrown by the given lambda function. But I don't know why this pattern is rarely seen in Java. Row-polymorphic error handling is essentially using row-polymorphism to handle the <code>throws E</code> part.</p>

CC BY-SA 4.0
2023-05-16T17:25:44.793Z
2023-08-04T02:18:03.290Z
2023-08-04T02:18:03.290Z
ice1000
CC BY-SA 4.0
2023-07-03T14:44:59.897Z
@theonlygusti I think it's already exemplified in the answer
ice1000
CC BY-SA 4.0
2023-07-26T20:41:13.363Z
@SilvioMayolo Also `Error` (sorry, the pedant in me had to comment even though your comment is two months old)
user
CC BY-SA 4.0
2023-05-16T17:34:38.700Z
Are there examples of checked exceptions (and row-polymorphic checked exceptions) in programming languages? Or is it "just" an "academic" concept?
Alex Nelson
CC BY-SA 4.0
2023-05-16T17:43:55.217Z
@AlexNelson Java (in)famously implements straightforward checked exceptions. It provides a backdoor in the form of `RuntimeException`, but if you ignore that, then every exception in Java is statically checked.
Silvio Mayolo
CC BY-SA 4.0
2023-05-16T17:46:36.007Z
@AlexNelson I'll add to my answer. I'm looking for the PDFs now.
ice1000
CC BY-SA 4.0
2023-05-16T17:47:37.680Z
@SilvioMayolo Ah, yes, I had happily forgotten Java did this. It has been a few years since I last used the language.
Alex Nelson
CC BY-SA 4.0
2023-05-17T11:02:36.907Z
I think there is another "C-style" option ─ the output of the function is the result you want, except it may be some other value (e.g. null or -1 or even a value that would potentially be a valid result) when an error occurs, and there is a separate function you are supposed to call to check if an error *did* occur. This approach is used in some APIs such as for OpenGL or MySQL, and it's also similar to how arithmetic works in assembly languages (i.e. you get a normal result in the place you expect, but there's also a carry flag you should check if you want to handle overflows).
kaya3
CC BY-SA 4.0
2024-08-17T15:20:18.390Z
The compiler description link is broken. Does anyone know name of the paper it was linked to?
osa1
CC BY-SA 4.0
2024-08-17T22:27:01.250Z
@osa1 I couldn't find it either, but you might also checkout the related thesis: https://arxiv.org/abs/0910.2654
ice1000
CC BY-SA 4.0
2024-08-18T07:33:56.957Z
I asked this on social media and someone posted the internet archive link: https://web.archive.org/web/20190824231320/https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.210.2810&rep=rep1&type=pdf You have to search in internet archive and then select "search archived web sites". By default it searches in "metadata" (whatever that is) and can't find it.
osa1
CC BY-SA 4.0
2024-08-18T20:21:28.833Z
Oh thank goodness!
ice1000
CC BY-SA 4.0
2024-08-21T17:48:27.837Z
I would add the other kind of error handling in C, which is return a result of the desired type, with a sentinel value indicating failure. e.g. returning -1 instead of a valid index when searching for something in a collection. This is similar to the monadic error way, except you don't have the compiler enforcing any of the checks for you, so you can easily forget to do it. (Not saying this is a good way to handle errors, just that it should be mentioned for completeness.)
CoffeeTableEspresso
CC BY-SA 4.0
2023-08-03T03:18:22.723Z
The comment "You can also have no error handling, which is what Go have." seems incorrect to me unless I'm misinterpreting something. Go uses a combination of error codes (using the languages multiple returns along with a return value), and an exception-like model with panic() (used with the languages defer mechanism), and these are used throughout the language ecosystem and the former has syntactic sugar specially designed for it.
JennyJams
CC BY-SA 4.0
2023-08-03T03:20:19.493Z
@JennyJams that is not the error handling in my vocabulary. If that counts as error handling, then I'd say every language that supports conditional statements have error handling, even including Turing machines and DFAs -- which kinda trivializes the discussion.
ice1000
CC BY-SA 4.0
2023-08-03T03:25:05.653Z
@ice1000 Doesn’t C error handling work the same way as Go error handling? I don’t understand the distinction you’re making between them.
Alexis King
CC BY-SA 4.0
2023-08-03T03:49:36.460Z
@AlexisKing In Go, the output of the function is in the return value, not in parameters.
ice1000
CC BY-SA 4.0
2023-08-03T03:53:04.770Z
@ice1000 That is more common in C than using out parameters.
Alexis King
CC BY-SA 4.0
2023-08-03T04:07:35.623Z
@ice1000 @alexis-king technically the distinction between Go `func foo() int, error {...}` and C `error foo(int* outparam){...}` is that in C the out parameter might potentially be a nullpointer, and that if the function fails and doesn't assign to the value pointed to by outparam, it might technically be undefined if it was not previously initialized, whereas in Go it would likely by that types zero value. But point aside, these are both error handling conventions not afaik enforced by the language, and with Go using this style pervasively it seems strange to claim it has no error handling.
JennyJams
CC BY-SA 4.0
2023-08-03T04:23:56.270Z
@JennyJams I'm convinced. But I am unsure how to describe Go's error handling. Could you please edit my answer to reveal your thoughts?
ice1000
CC BY-SA 4.0
2023-08-03T05:09:07.383Z
@ice1000 sure, I can edit the answer. The critique I had with the answer is that golang _does_ have an error modelling system; I could change it to "You could also have no error handling, and simply abort the process when a runtime error is encountered".

A description of golang would mostly be describing it as a combination of error-code returns like C, and the panic/recover mechanism (basically exceptions), and while parts of that system overall (defer, multiple return) are fairly rare among most PL -- maybe I could add a comment on how systems can use multiple styles of error handling?
JennyJams
<h1>Algebraic effects</h1>
<p>This is a concept I learned relatively recently, and it’s a concept found exclusively in academic and functional languages for now. Effectively, it is a resumable exception. When you “raise” an effect, you save a continuation at the point of raising. When it is handled, you can resume that continuation, effectively acting like a resumable exception.</p>
<p>However, they can be also be used to implement generators and coroutines, among other things. Dependency injection using them is a breeze. <a href="https://overreacted.io/algebraic-effects-for-the-rest-of-us/" rel="noreferrer">This blog post</a> is a great guide on algebraic effects.</p>

CC BY-SA 4.0
2023-05-16T18:20:45.830Z
2023-05-16T18:24:35.603Z
2023-05-16T18:24:35.603Z
Seggan
CC BY-SA 4.0
2023-07-03T14:46:42.590Z
I would like to point out that this is actually monadic effects. Algebraic effects feature in composability, in addition to resumability of the exceptions.
ice1000
CC BY-SA 4.0
2023-07-15T01:44:21.763Z
@ice1000 feel free to edit an explanation in, i don't really see what ure talking about
Seggan
CC BY-SA 4.0
2023-05-16T18:23:34.347Z
These are incredibly cool for way more than just errors. For example, dependency injection of things like logging functions.
rydwolf
<h3><a href="https://en.wikipedia.org/wiki/Interrupt" rel="noreferrer">Interrupts</a></h3>
<p>especially the Processor / Software interrupts. It temporarily stops execution of the current thread, handles the event and (optionally) resumes the thread.</p>
<p><a href="https://i.sstatic.net/bwMMB.png" rel="noreferrer"><img src="https://i.sstatic.net/bwMMB.png" alt="enter image description here" /></a><br />
(<a href="https://en.wikipedia.org/wiki/Interrupt" rel="noreferrer">source</a>)</p>

CC BY-SA 4.0
2023-05-16T17:28:41.020Z
2023-05-16T17:28:41.020Z
Glorfindel
<p>A great blog post with many different techniques is <a href="https://joeduffyblog.com/2016/02/07/the-error-model/" rel="nofollow noreferrer">The [Midori] Error Model</a> by Joe Duffy.</p>
<p>In addition to that and the other answers, I want to add Lisp conditions and restarts.</p>
<p>See <a href="https://z0ltan.wordpress.com/2016/08/06/conditions-and-restarts-in-common-lisp/" rel="nofollow noreferrer">here</a>, <a href="https://lisper.in/restarts" rel="nofollow noreferrer">here</a>, and <a href="https://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html" rel="nofollow noreferrer">here</a> for explanations, but the gist is that instead of unwinding the stack to callers, those callers register handlers that are invoked <em>at the spot of the error.</em> Those handlers can choose to handle the error or hand it off to the next handler.</p>
<p>And on top of that, if a handler handles an error, it can possibly allow <em>restarting</em>.</p>
<p>I'll try to explain with an example.</p>
<p>Say you have a program, and in that program is a cache that can grow without bound. Also, pretend that you need to allocate memory explicitly for some reason.</p>
<p>Now, say the program is running, but at some point, memory allocation fails. The program previously registered a handler that basically evicts the oldest items from the cache, marks the error handled, and says that a restart can happen.</p>
<p>Instead of just returning failure, your memory allocation code will run the handler, evict stuff from the cache, and try again, succeed, and return the allocation.</p>
<p>There was no unwinding of the stack, so the code asking for the allocation is none the wiser and continues on its merry way.</p>
<p>It turns out that <em>not</em> unwinding the stack is powerful because you can handle the error without information being lost or state being destroyed. And since handlers can be registered by callers, it's as though callers are still handling the error.</p>
<p>This is not theoretical, or even limited to Lisp, by the way. I implemented conditions and restarts in C. My <code>malloc()</code> will handle memory allocation and attempt to handle failure and retry.</p>

CC BY-SA 4.0
2023-05-16T20:28:19.243Z
2024-10-07T16:29:01.923Z
2024-10-07T16:29:01.923Z
Gavin D. Howard
CC BY-SA 4.0
2023-08-01T16:38:11.160Z
I didn't see your answer, sorry
coredump
CC BY-SA 4.0
2023-10-23T16:36:33.897Z
Could you give other scenarios where resumption is useful?
Pablo H
CC BY-SA 4.0
2023-10-23T16:37:42.283Z
Also, how does the code using the cache know that there's a memory allocation 7 levels deep in the call tree?
Pablo H
CC BY-SA 4.0
2023-10-23T22:56:16.783Z
@PabloH another situation where resumption is useful: you're trying to connect to a CDN or another network resource, but the first try fails. The error handler could try to connect to a backup server instead.
Gavin D. Howard
CC BY-SA 4.0
2023-10-23T22:59:01.987Z
@PabloH, the code using the cache will not know about the memory allocation, so it registers an error handler first, then continues its work. When the memory allocation happens 7 levels deep, and fails, that error handler is called. By virtue of being called, it knows there is an allocation failure, and because it was registered by the cache code, it presumably knows about the cache too.
Gavin D. Howard
CC BY-SA 4.0
2023-10-25T15:26:26.757Z
Thanks. Very clear. Then the error handler is basically a global variable, with the usual pros and cons. Though memory allocation _is_ a rather global issue... I'll meditate about this. :-)
Pablo H
CC BY-SA 4.0
2023-10-25T15:57:01.590Z
@PabloH, it doesn't have to be global. In my C code, a function pointer is stored in a stack local to the thread. Then when an error occurs, each handler in the stack is run starting from the most recent, until a handler says that it handled the error. So if the cache has nothing in it, it's error handler would have to say, "Can't handle this, sorry." Also, a function may register a handler (push it on the error handler stack), but before it returns, it also needs to pop the error handler off of the stack. I abused some macros to do this.
Gavin D. Howard
<p>.NET, so C# and Visual Basic (and many other languages probably have similar concepts) allow applications to monitor unhandled exceptions, i.e. those which haven't been caught in a try-catch block. There's an <a href="https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception?view=net-7.0" rel="noreferrer"><code>UnhandledException</code> event</a> for that. In most cases, it won't let you resume the program, though.</p>

CC BY-SA 4.0
2023-05-16T17:41:00.077Z
2023-05-16T17:41:00.077Z
Glorfindel
<h3>Resumable condition system</h3>
<p>This is the system implemented in Common Lisp, where you can have resumable
exceptions.</p>
<p>You raise an exception by <em>signaling</em> a <em>condition</em>:</p>
<pre><code>(signal condition ...args)
</code></pre>
<p>Some conditions are serious and enters the debugger if nobody handles them, they are raised by calling <code>error</code>:</p>
<pre><code>(error condition ...args)
</code></pre>
<p>In particular, for simple errors <code>condition</code> can be a format string:</p>
<pre><code>(error &quot;no such file: ~s&quot; file)
</code></pre>
<p>You can define handlers that execute code when some condition type matches:</p>
<pre><code>(handler-bind ((some-condition (lambda (c) ...))
               (other-condition (lambda (c) ...)))
  ...)
</code></pre>
<p>When an exception of type <code>some-condition</code> is signaled, the associated handler is run directly starting from the call stack <em>where the condition is signaled</em>: there is no stack unwinding at this step. That makes error handling a bit more flexible.</p>
<p>The error handler needs to make a non-local exit, otherwise it is said that the handler <em>declines</em> to handle the signal, and the signal bubbles up to other potential handlers. A non-local exit is for example a return from a block lexically enclosing the handler:</p>
<p>(block :main
(handler-bind ((c (lambda (_) (return-from :main &quot;oh no&quot;))))
...))</p>
<p>In that case, exiting from the closure unwinds the stack, just like exception usually do in other languages. There is in fact a <code>handler-case</code> that automatically performs the unwinding:</p>
<pre><code>(handler-case ...
  (c (_) &quot;oh no&quot;))    
</code></pre>
<p>Here the body and the catch clauses are reversed, but in addition to that, the code evaluated for <code>c</code> is executed in the context of the code being caught, after the stack was unwound.</p>
<p>You can also define <em>restarts</em>:</p>
<pre><code>(restart-case some-expr
  (my-restart () :report &quot;Return 10&quot;
    10)
  (ignore () :report &quot;Ignore error and return NIL&quot;))
</code></pre>
<p>A restart expression defines a point in your code that your signal handler can invoke:</p>
<pre><code>(handler-bind ((t (lambda (c) (invoke-restart 'my-restart))))
  (restart-case (error &quot;Who could have foreseen that?!&quot;)
    (my-restart () 10)))
</code></pre>
<p>Here, <code>(error ...)</code> signals an error and enter the debugger if no-one handles it. But the surrounding handler is executed, and invoke <code>my-restart</code>, so the flow of execution resumes from the restart clause that matches <code>my-restart</code>, which evaluates to 10.</p>
<p>The error was handled by executing a different action. Typically, when you are executing code and have an error, you enter the debugger which displays the various restarts. Suppose you write a small game with an <code>update</code> function:</p>
<pre><code>(defun update (world)
  (destructuring-bind (&amp;key (x 0) (y 0)) world
    (list :x (1+ x) :y (1+ y))))
</code></pre>
<p>You can define the game loop as follows:</p>
<pre><code>(let ((world '(:x 0 :y 0)))
  (loop 
    (restart-case (setf world (update world))
      (retry ()
        :report &quot;Try again&quot;)
      (exit ()
        :report &quot;Exit game loop&quot;
        (return)))))
</code></pre>
<p>Then you can change the <code>update</code> function while the main loop runs, and if it contains bugs, you can fix it and retry executing the code without forgetting the context.</p>
<p>For example, if I introduce a bug while the game loops:</p>
<pre><code>(defun update (world)
  (error &quot;No&quot;))
</code></pre>
<p>Then the debugger prints:</p>
<pre><code>No
   [Condition of type SIMPLE-ERROR]

Restarts:
 0: [RETRY] Try again
 1: [EXIT] Exit game loop
 2: [ABORT] Abort compilation.
 3: [*ABORT] Return to SLIME's top level.
 4: [ABORT] abort thread (#&lt;THREAD &quot;worker&quot; RUNNING {1021890003}&gt;)

Backtrace:
  0: (UPDATE #&lt;unused argument&gt;)
      [No Locals]
  1: ((SB-C::TOP-LEVEL-FORM (LET ((WORLD (QUOTE #1=#))) (LOOP (RESTART-CASE (SETF WORLD #1#) (RETRY NIL :REPORT &quot;Try again&quot;) (EXIT NIL :REPORT &quot;Exit game loop&quot; #1#)))))) [toplevel]
      Locals:
        WORLD = (:X 329515384 :Y 329515384)
</code></pre>
<p>I can execute code in the debugger, change the <code>WORLD</code> variable, or exit the loop by invoking the EXIT restart. If I recompile <code>update</code> I can also use the new version of the function with the current <code>WORLD</code> state.</p>

CC BY-SA 4.0
2023-08-01T16:37:30.263Z
2024-08-16T22:01:38.997Z
2024-08-16T22:01:38.997Z
coredump
<h2>Setting a global error code</h2>
<p>I would not recommend this in most cases for multiple reasons I state at the end of this answer. But I didn't see anyone mention it yet. And because it is a runtime error handling system that is being used in the real-world, I think it should not remain unmentioned.</p>
<p>The idea is that when an error occurs, then an error code is written to a global variable, and then the program execution continues as if nothing happened. To handle the error, you just check the value of that variable after performing the operation.</p>
<p>One language that works that way is the SAP-proprietary language ABAP. For example, looking up a value in a database table looks like this:</p>
<pre><code>SELECT SINGLE name FROM customers INTO customer_name WHERE id = customer_id.
IF sy-subrc = 0.
    WRITE: 'The customer name is', customer_name.
ELSE.
    WRITE 'Customer not found'.
ENDIF.
</code></pre>
<p>The line beginning with <code>SELECT</code> reads from the database. When the query doesn't return any rows, it set the global variable <code>sy-subrc</code> to a value other than 0, and then the program continues. If the programmer would like to catch that error, they would then immediately check the value of that variable.</p>
<p><strong>Advantage:</strong></p>
<ul>
<li>Doesn't need any new syntax constructs except two you probably have anyway: global variables and branches.</li>
<li>When programmers don't need error handling, they don't need to write any.</li>
</ul>
<p><strong>Disadvantages:</strong></p>
<ul>
<li>It is really easy to forget handling errors at all. Just ignoring that an operation could have failed and continuing the program execution can wreak havok.</li>
<li>The documentation of every operation has to mention if it can set error codes and if it does what they mean. Programmers need to memorize that or constantly look it up.</li>
<li>The code for handling errors must come right after the operation. The programmers can't move it out of the way like they can with, say, the try..catch pattern.</li>
<li>Moving error handling code around can result in inadvertently putting some code between the operation and the error checking that overwrites the global error code.</li>
<li>Doesn't play well with concurrency. Sharing one global error code between threads would be a recipe for disaster. So you need to find some other way to handle errors happening in concurrent operations.</li>
</ul>
<p>Which is why ABAP, the language I used as an example here, moves away from this paradigm in the past couple decades in favor of class-based exception handling using <code>TRY...CATCH</code>. But old mistakes are difficult to correct if you don't want to break any old code.</p>

CC BY-SA 4.0
2023-07-14T10:49:28.727Z
2023-07-14T11:10:54.900Z
2023-07-14T11:10:54.900Z
Philipp
<h1>Go-style:</h1>
<p>If you have written any Go code you have probably encountered the built-in error type. Go code uses error values to indicate an abnormal state. For example, the <code>os.Open</code> function returns a non-nil error value when it fails to open a file.</p>
<pre class="lang-go prettyprint-override"><code>func Open(name string) (file *File, err error)
</code></pre>
<p>The following code uses <code>os.Open</code> to open a file. If an error occurs it calls <code>log.Fatal</code> to print the error message and stop.</p>
<pre class="lang-go prettyprint-override"><code>f, err := os.Open(&quot;filename.ext&quot;)
if err != nil {
    log.Fatal(err)
}
</code></pre>
<p>...</p>
<p>PS: from <a href="https://go.dev/blog/error-handling-and-go" rel="noreferrer">https://go.dev/blog/error-handling-and-go</a></p>

CC BY-SA 4.0
2023-06-06T07:49:22.757Z
2023-06-06T07:49:22.757Z
чистов_n
<h1>Configurable and expressive</h1>
<p>Library code may use C or Go style error signaling, with minimal or no allocation in case of errors. Business logic code may be interested in stack traced exceptions that interrupt code flow.</p>
<p>So... why not have both?</p>
<p>On library code:</p>
<pre class="lang-python prettyprint-override"><code>var err as error.return;

def func( arg int ) ( int | err )
{
    var ret, err = funcB( arg );
    return ret;
}
</code></pre>
<p>Desugars to:</p>
<pre class="lang-python prettyprint-override"><code>def func( arg int ) ( int | err )
{
    var ret, err = funcB( arg );
    if ( err != null )               // Autogenerated
        return err;                  // Autogenerated
    return ret;
}
</code></pre>
<p>And on code that is not expected to progress after an error:</p>
<pre class="lang-python prettyprint-override"><code>var err as error.throw;

def func( arg int ) int
{
    var ret, err = funcB( arg );
    return ret;
}

</code></pre>
<p>Desugars to:</p>
<pre class="lang-python prettyprint-override"><code>def func( arg int ) ( int )
{
    var ret, err = funcB( arg );
    if ( err )                                    // Autogenerated
    {                                             // Autogenerated
        err.pushStackInfo( __FILE__ , __LINE__ ); // Autogenerated
        throw err;                                // Autogenerated
    }
    return ret;
}
</code></pre>
<p><strong>Bonus:</strong> A compiler option may cause <code>pushStackInfo()</code> to be compiled as no-op, so to omit (allocating) call stacks in some builds.</p>

CC BY-SA 4.0
2023-07-14T15:41:31.693Z
2024-08-16T22:52:40.520Z
2024-08-16T22:52:40.520Z
André L F S Bacci
<h1>Union Types</h1>
<p>Some languages like <a href="https://ziglang.org/documentation/master/#Errors" rel="noreferrer">Zig</a> handle errors via union types.
The return value of expressions which may error can be <em>either</em> the intended value, or an Error value. To use the intended return of these expressions, one must handle the error. (Either catching it, or re-throwing it).</p>

CC BY-SA 4.0
2023-05-25T23:54:06.827Z
2023-05-30T18:14:42.523Z
2023-05-30T18:14:42.523Z
ATaco
CC BY-SA 4.0
2023-05-25T23:59:56.843Z
It looks like there's already an answer that mentions `Result<Success, Failure>`-like types. Is Zig's type substantially different from the one in Swift and Rust?
Bbrk24
CC BY-SA 4.0
2023-05-26T00:46:33.907Z
Zig's is using Language-level Union-types rather than a Type which implements Union Types. I think the difference is worth noting for languages that support it. Union types don't necessarily have the same restrictions as a `Result<Success, Failure>` type.
ATaco
# What are different ways of handling runtime errors?
Posted by: username_0 on 2023-05-16
Most languages have a system where exceptions are classes and you can `throw` them then use a `try/catch` block to handle them.

What alternate runtime error handling systems are used, and what are their benefits and drawbacks?
**Tags:** language-comparison error-handling
> **Comments:**
> **username_1**: For those who see this, it's best to allow the original author of an example question to post it; this was a slight misunderstanding, and I give my blessing to mousetail to post it.  
---
## 13 answers
### Answer by username_9 on 2023-05-16. Score: 43
Monadic errors, like Result in Rust. You need to manually catch the error in order to get the result.
Exceptions, like in Kotlin/Scala/Python/C#. By default, you use the return value, and you can optionally catch the error encountered.
Checked exceptions. Like exceptions, but statically check which exceptions are propagated and which are caught.
Row-polymorphic checked exceptions. Like checked exceptions, but use row polymorphism to allow higher-order checked exceptions to "flow-through" higher-order functions. Video and compiler description (see page 34)

Checked exceptions can be seen as first-order version of row-polymorphic exceptions


C-style error handling. Return value of a function is error code, and the output of the function is stored in a pointer allocated by the caller. This is a very good approach for languages that needs programmers to manage memory, because we don't have to worry about deallocation of return values as library authors.

You could also have no error handling, and abort the process if any runtime error is encountered.
A short note on checked exceptions: you may use generics to have higher order functions like this:
@FunctionalInterface
interface Fn {
  V apply(T t) throws E;
}

public static  V modusPonens(Fn f, T t) throws E {
  return f.apply(t);
}

So that the exception thrown for modusPonens depends on the exception thrown by the given lambda function. But I don't know why this pattern is rarely seen in Java. Row-polymorphic error handling is essentially using row-polymorphism to handle the throws E part.
> **Comments:**
> **username_10** (2023-05-16): Are there examples of checked exceptions (and row-polymorphic checked exceptions) in programming languages? Or is it "just" an "academic" concept?  
> **username_11** (2023-05-16): @AlexNelson Java (in)famously implements straightforward checked exceptions. It provides a backdoor in the form of `RuntimeException`, but if you ignore that, then every exception in Java is statically checked.  
> **username_9** (2023-05-16): @AlexNelson I'll add to my answer. I'm looking for the PDFs now.  
> **username_10** (2023-05-16): @SilvioMayolo Ah, yes, I had happily forgotten Java did this. It has been a few years since I last used the language.  
> **username_12** (2023-05-17): I think there is another "C-style" option ─ the output of the function is the result you want, except it may be some other value (e.g. null or -1 or even a value that would potentially be a valid result) when an error occurs, and there is a separate function you are supposed to call to check if an error *did* occur. This approach is used in some APIs such as for OpenGL or MySQL, and it's also similar to how arithmetic works in assembly languages (i.e. you get a normal result in the place you expect, but there's also a carry flag you should check if you want to handle overflows).  
> **username_9** (2023-07-03): @theonlygusti I think it's already exemplified in the answer  
> **username_3** (2023-07-26): @SilvioMayolo Also `Error` (sorry, the pedant in me had to comment even though your comment is two months old)  
> **username_15** (2023-08-03): The comment "You can also have no error handling, which is what Go have." seems incorrect to me unless I'm misinterpreting something. Go uses a combination of error codes (using the languages multiple returns along with a return value), and an exception-like model with panic() (used with the languages defer mechanism), and these are used throughout the language ecosystem and the former has syntactic sugar specially designed for it.  
> **username_9** (2023-08-03): @JennyJams that is not the error handling in my vocabulary. If that counts as error handling, then I'd say every language that supports conditional statements have error handling, even including Turing machines and DFAs -- which kinda trivializes the discussion.  
> **username_16** (2023-08-03): @ice1000 Doesn’t C error handling work the same way as Go error handling? I don’t understand the distinction you’re making between them.  
> **username_9** (2023-08-03): @AlexisKing In Go, the output of the function is in the return value, not in parameters.  
> **username_16** (2023-08-03): @ice1000 That is more common in C than using out parameters.  
> **username_15** (2023-08-03): @ice1000 @alexis-king technically the distinction between Go `func foo() int, error {...}` and C `error foo(int* outparam){...}` is that in C the out parameter might potentially be a nullpointer, and that if the function fails and doesn't assign to the value pointed to by outparam, it might technically be undefined if it was not previously initialized, whereas in Go it would likely by that types zero value. But point aside, these are both error handling conventions not afaik enforced by the language, and with Go using this style pervasively it seems strange to claim it has no error handling.  
> **username_9** (2023-08-03): @JennyJams I'm convinced. But I am unsure how to describe Go's error handling. Could you please edit my answer to reveal your thoughts?  
> **username_15** (2023-08-03): @ice1000 sure, I can edit the answer. The critique I had with the answer is that golang _does_ have an error modelling system; I could change it to "You could also have no error handling, and simply abort the process when a runtime error is encountered".

A description of golang would mostly be describing it as a combination of error-code returns like C, and the panic/recover mechanism (basically exceptions), and while parts of that system overall (defer, multiple return) are fairly rare among most PL -- maybe I could add a comment on how systems can use multiple styles of error handling?  
> **username_13** (2024-08-17): The compiler description link is broken. Does anyone know name of the paper it was linked to?  
> **username_9** (2024-08-17): @osa1 I couldn't find it either, but you might also checkout the related thesis: https://arxiv.org/abs/0910.2654  
> **username_13** (2024-08-18): I asked this on social media and someone posted the internet archive link: https://web.archive.org/web/20190824231320/https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.210.2810&rep=rep1&type=pdf You have to search in internet archive and then select "search archived web sites". By default it searches in "metadata" (whatever that is) and can't find it.  
> **username_9** (2024-08-18): Oh thank goodness!  
> **username_14** (2024-08-21): I would add the other kind of error handling in C, which is return a result of the desired type, with a sentinel value indicating failure. e.g. returning -1 instead of a valid index when searching for something in a collection. This is similar to the monadic error way, except you don't have the compiler enforcing any of the checks for you, so you can easily forget to do it. (Not saying this is a good way to handle errors, just that it should be mentioned for completeness.)  
---
### Answer by username_1 on 2023-05-16. Score: 21
# Algebraic effects

This is a concept I learned relatively recently, and it’s a concept found exclusively in academic and functional languages for now. Effectively, it is a resumable exception. When you “raise” an effect, you save a continuation at the point of raising. When it is handled, you can resume that continuation, effectively acting like a resumable exception.

However, they can be also be used to implement generators and coroutines, among other things. Dependency injection using them is a breeze. [This blog post](https://overreacted.io/algebraic-effects-for-the-rest-of-us/) is a great guide on algebraic effects.
> **Comments:**
> **username_17** (2023-05-16): These are incredibly cool for way more than just errors. For example, dependency injection of things like logging functions.  
> **username_9** (2023-07-03): I would like to point out that this is actually monadic effects. Algebraic effects feature in composability, in addition to resumability of the exceptions.  
> **username_1** (2023-07-15): @ice1000 feel free to edit an explanation in, i don't really see what ure talking about  
---
### Answer by username_18 on 2023-05-16. Score: 20
### [Interrupts](https://en.wikipedia.org/wiki/Interrupt)

especially the Processor / Software interrupts. It temporarily stops execution of the current thread, handles the event and (optionally) resumes the thread.

[![enter image description here](https://i.sstatic.net/bwMMB.png)](https://i.sstatic.net/bwMMB.png)  
([source](https://en.wikipedia.org/wiki/Interrupt))
---
### Answer by username_19 on 2023-05-16. Score: 17
A great blog post with many different techniques is [The [Midori] Error Model](https://joeduffyblog.com/2016/02/07/the-error-model/) by Joe Duffy.

In addition to that and the other answers, I want to add Lisp conditions and restarts.

See [here](https://z0ltan.wordpress.com/2016/08/06/conditions-and-restarts-in-common-lisp/), [here](https://lisper.in/restarts), and [here](https://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html) for explanations, but the gist is that instead of unwinding the stack to callers, those callers register handlers that are invoked *at the spot of the error.* Those handlers can choose to handle the error or hand it off to the next handler.

And on top of that, if a handler handles an error, it can possibly allow *restarting*.

I'll try to explain with an example.

Say you have a program, and in that program is a cache that can grow without bound. Also, pretend that you need to allocate memory explicitly for some reason.

Now, say the program is running, but at some point, memory allocation fails. The program previously registered a handler that basically evicts the oldest items from the cache, marks the error handled, and says that a restart can happen.

Instead of just returning failure, your memory allocation code will run the handler, evict stuff from the cache, and try again, succeed, and return the allocation.

There was no unwinding of the stack, so the code asking for the allocation is none the wiser and continues on its merry way.

It turns out that *not* unwinding the stack is powerful because you can handle the error without information being lost or state being destroyed. And since handlers can be registered by callers, it's as though callers are still handling the error.

This is not theoretical, or even limited to Lisp, by the way. I implemented conditions and restarts in C. My `malloc()` will handle memory allocation and attempt to handle failure and retry.
> **Comments:**
> **username_20** (2023-08-01): I didn't see your answer, sorry  
> **username_21** (2023-10-23): Could you give other scenarios where resumption is useful?  
> **username_21** (2023-10-23): Also, how does the code using the cache know that there's a memory allocation 7 levels deep in the call tree?  
> **username_19** (2023-10-23): @PabloH another situation where resumption is useful: you're trying to connect to a CDN or another network resource, but the first try fails. The error handler could try to connect to a backup server instead.  
> **username_19** (2023-10-23): @PabloH, the code using the cache will not know about the memory allocation, so it registers an error handler first, then continues its work. When the memory allocation happens 7 levels deep, and fails, that error handler is called. By virtue of being called, it knows there is an allocation failure, and because it was registered by the cache code, it presumably knows about the cache too.  
> **username_21** (2023-10-25): Thanks. Very clear. Then the error handler is basically a global variable, with the usual pros and cons. Though memory allocation _is_ a rather global issue... I'll meditate about this. :-)  
> **username_19** (2023-10-25): @PabloH, it doesn't have to be global. In my C code, a function pointer is stored in a stack local to the thread. Then when an error occurs, each handler in the stack is run starting from the most recent, until a handler says that it handled the error. So if the cache has nothing in it, it's error handler would have to say, "Can't handle this, sorry." Also, a function may register a handler (push it on the error handler stack), but before it returns, it also needs to pop the error handler off of the stack. I abused some macros to do this.  
---
### Answer by username_2 on 2023-05-16. Score: 13
# Aborts/Panics

This is the system Rust uses: There's a "result" type (`Result`) for recoverable errors, like a malformed URL in a HTTP library, and an "abort" function/macro (`panic!`) for unrecoverable errors like indexing out of an array. Recoverable errors are returned from functions and can be processed like any other type, but unrecoverable errors crash the program instantly.
> **Comments:**
> **username_0** (2023-05-16): That's not true though, you can write a panic handler in rust  
> **username_3** (2023-05-20): As mousetail said, there's stuff like [`catch_unwind`](https://doc.rust-lang.org/std/panic/fn.catch_unwind.html) (doesn't catch all panics, though)  
> **username_4** (2023-05-30): Lua also has sort of panics. `error(msg)` and `pcall(...)`  
---
### Answer by username_5 on 2023-05-16. Score: 9
Out-params
In Objective-C, idiomatic error handling uses an extra parameter:
error:(NSError * _Nullable *)error;

This allows you to suppress errors by passing NULL for that parameter, but also requires a bit more setup for both the caller (have to declare NSError *error; and then pass &error) and the callee (have to check if (error != NULL) before writing to it).
---
### Answer by username_6 on 2023-05-17. Score: 9
In C
errno
Many functions in C such as malloc() or I/O functions such as fwrite() will return an error status such as NULL or -1 on error and set the global error state errno to a error code describing the error.
setjmp()/longjmp()
A common way to implement exception handling in C is with setjmp() and longjmp(). Here is an example:
#include 
jmp_buf buf;
void throws() {
    // ...
    if (error_occurred) {
        longjmp(buf, -1);
    }
}
int main() {
    int err = setjmp(buf);
    if (err) { // If this point was reached by the longjmp() call
        handle_error(err);
        return;
    }
    // ...
}
> **Comments:**
> **username_5** (2023-07-03): Your use of `setjmp` there actually isn't permissible under the standard. You can only use it in a control-flow conditional (`if (setjmp())`/`switch (setjmp())`/`while (setjmp())`), apply it directly to a comparison or boolean operator (`!setjmp()`/`setjmp() > 0`), or discard the result (`(void)setjmp();`).  
> **username_7** (2023-07-14): Technically, [`errno`](https://en.cppreference.com/w/c/error/errno) is defined as a macro, and thus it's like it's in the global namespace, but its value is thread-local.  
> **username_8** (2024-12-23): @Bbrk24: The Standard fails to require that implementations support the construct, but there's no sign that the authors intended that implementations not support it if practical, and I am unaware of any implementations that can't handle that use case.  
---
### Answer by username_18 on 2023-05-16. Score: 7
.NET, so C# and Visual Basic (and many other languages probably have similar concepts) allow applications to monitor unhandled exceptions, i.e. those which haven't been caught in a try-catch block. There's an [`UnhandledException` event](https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception?view=net-7.0) for that. In most cases, it won't let you resume the program, though.
---
### Answer by username_25 on 2023-05-25. Score: 7
# Union Types

Some languages like [Zig](https://ziglang.org/documentation/master/#Errors) handle errors via union types.
The return value of expressions which may error can be *either* the intended value, or an Error value. To use the intended return of these expressions, one must handle the error. (Either catching it, or re-throwing it).
> **Comments:**
> **username_5** (2023-05-25): It looks like there's already an answer that mentions `Result<Success, Failure>`-like types. Is Zig's type substantially different from the one in Swift and Rust?  
> **username_25** (2023-05-26): Zig's is using Language-level Union-types rather than a Type which implements Union Types. I think the difference is worth noting for languages that support it. Union types don't necessarily have the same restrictions as a `Result<Success, Failure>` type.  
---
### Answer by username_20 on 2023-08-01. Score: 6
Resumable condition system
This is the system implemented in Common Lisp, where you can have resumable
exceptions.
You raise an exception by signaling a condition:
(signal condition ...args)

Some conditions are serious and enters the debugger if nobody handles them, they are raised by calling error:
(error condition ...args)

In particular, for simple errors condition can be a format string:
(error "no such file: ~s" file)

You can define handlers that execute code when some condition type matches:
(handler-bind ((some-condition (lambda (c) ...))
               (other-condition (lambda (c) ...)))
  ...)

When an exception of type some-condition is signaled, the associated handler is run directly starting from the call stack where the condition is signaled: there is no stack unwinding at this step. That makes error handling a bit more flexible.
The error handler needs to make a non-local exit, otherwise it is said that the handler declines to handle the signal, and the signal bubbles up to other potential handlers. A non-local exit is for example a return from a block lexically enclosing the handler:
(block :main
(handler-bind ((c (lambda (_) (return-from :main "oh no"))))
...))
In that case, exiting from the closure unwinds the stack, just like exception usually do in other languages. There is in fact a handler-case that automatically performs the unwinding:
(handler-case ...
  (c (_) "oh no"))    

Here the body and the catch clauses are reversed, but in addition to that, the code evaluated for c is executed in the context of the code being caught, after the stack was unwound.
You can also define restarts:
(restart-case some-expr
  (my-restart () :report "Return 10"
    10)
  (ignore () :report "Ignore error and return NIL"))

A restart expression defines a point in your code that your signal handler can invoke:
(handler-bind ((t (lambda (c) (invoke-restart 'my-restart))))
  (restart-case (error "Who could have foreseen that?!")
    (my-restart () 10)))

Here, (error ...) signals an error and enter the debugger if no-one handles it. But the surrounding handler is executed, and invoke my-restart, so the flow of execution resumes from the restart clause that matches my-restart, which evaluates to 10.
The error was handled by executing a different action. Typically, when you are executing code and have an error, you enter the debugger which displays the various restarts. Suppose you write a small game with an update function:
(defun update (world)
  (destructuring-bind (&key (x 0) (y 0)) world
    (list :x (1+ x) :y (1+ y))))

You can define the game loop as follows:
(let ((world '(:x 0 :y 0)))
  (loop 
    (restart-case (setf world (update world))
      (retry ()
        :report "Try again")
      (exit ()
        :report "Exit game loop"
        (return)))))

Then you can change the update function while the main loop runs, and if it contains bugs, you can fix it and retry executing the code without forgetting the context.
For example, if I introduce a bug while the game loops:
(defun update (world)
  (error "No"))

Then the debugger prints:
No
   [Condition of type SIMPLE-ERROR]

Restarts:
 0: [RETRY] Try again
 1: [EXIT] Exit game loop
 2: [ABORT] Abort compilation.
 3: [*ABORT] Return to SLIME's top level.
 4: [ABORT] abort thread (#)

Backtrace:
  0: (UPDATE #)
      [No Locals]
  1: ((SB-C::TOP-LEVEL-FORM (LET ((WORLD (QUOTE #1=#))) (LOOP (RESTART-CASE (SETF WORLD #1#) (RETRY NIL :REPORT "Try again") (EXIT NIL :REPORT "Exit game loop" #1#)))))) [toplevel]
      Locals:
        WORLD = (:X 329515384 :Y 329515384)

I can execute code in the debugger, change the WORLD variable, or exit the loop by invoking the EXIT restart. If I recompile update I can also use the new version of the function with the current WORLD state.
---
### Answer by username_22 on 2023-07-14. Score: 6
Setting a global error code
I would not recommend this in most cases for multiple reasons I state at the end of this answer. But I didn't see anyone mention it yet. And because it is a runtime error handling system that is being used in the real-world, I think it should not remain unmentioned.
The idea is that when an error occurs, then an error code is written to a global variable, and then the program execution continues as if nothing happened. To handle the error, you just check the value of that variable after performing the operation.
One language that works that way is the SAP-proprietary language ABAP. For example, looking up a value in a database table looks like this:
SELECT SINGLE name FROM customers INTO customer_name WHERE id = customer_id.
IF sy-subrc = 0.
    WRITE: 'The customer name is', customer_name.
ELSE.
    WRITE 'Customer not found'.
ENDIF.

The line beginning with SELECT reads from the database. When the query doesn't return any rows, it set the global variable sy-subrc to a value other than 0, and then the program continues. If the programmer would like to catch that error, they would then immediately check the value of that variable.
Advantage:

Doesn't need any new syntax constructs except two you probably have anyway: global variables and branches.
When programmers don't need error handling, they don't need to write any.

Disadvantages:

It is really easy to forget handling errors at all. Just ignoring that an operation could have failed and continuing the program execution can wreak havok.
The documentation of every operation has to mention if it can set error codes and if it does what they mean. Programmers need to memorize that or constantly look it up.
The code for handling errors must come right after the operation. The programmers can't move it out of the way like they can with, say, the try..catch pattern.
Moving error handling code around can result in inadvertently putting some code between the operation and the error checking that overwrites the global error code.
Doesn't play well with concurrency. Sharing one global error code between threads would be a recipe for disaster. So you need to find some other way to handle errors happening in concurrent operations.

Which is why ABAP, the language I used as an example here, moves away from this paradigm in the past couple decades in favor of class-based exception handling using TRY...CATCH. But old mistakes are difficult to correct if you don't want to break any old code.
---
### Answer by username_23 on 2023-06-06. Score: 5
Go-style:
If you have written any Go code you have probably encountered the built-in error type. Go code uses error values to indicate an abnormal state. For example, the os.Open function returns a non-nil error value when it fails to open a file.
func Open(name string) (file *File, err error)

The following code uses os.Open to open a file. If an error occurs it calls log.Fatal to print the error message and stop.
f, err := os.Open("filename.ext")
if err != nil {
    log.Fatal(err)
}

...
PS: from https://go.dev/blog/error-handling-and-go
---
### Answer by username_24 on 2023-07-14. Score: 4
Configurable and expressive
Library code may use C or Go style error signaling, with minimal or no allocation in case of errors. Business logic code may be interested in stack traced exceptions that interrupt code flow.
So... why not have both?
On library code:
var err as error.return;

def func( arg int ) ( int | err )
{
    var ret, err = funcB( arg );
    return ret;
}

Desugars to:
def func( arg int ) ( int | err )
{
    var ret, err = funcB( arg );
    if ( err != null )               // Autogenerated
        return err;                  // Autogenerated
    return ret;
}

And on code that is not expected to progress after an error:
var err as error.throw;

def func( arg int ) int
{
    var ret, err = funcB( arg );
    return ret;
}


Desugars to:
def func( arg int ) ( int )
{
    var ret, err = funcB( arg );
    if ( err )                                    // Autogenerated
    {                                             // Autogenerated
        err.pushStackInfo( __FILE__ , __LINE__ ); // Autogenerated
        throw err;                                // Autogenerated
    }
    return ret;
}

Bonus: A compiler option may cause pushStackInfo() to be compiled as no-op, so to omit (allocating) call stacks in some builds.
---
