C++ Logo

std-proposals

Advanced search

Re: [std-proposals] fine-control exception/error/UB handling on function boundary

From: Tiago Freire <tmiguelf_at_[hidden]>
Date: Sat, 29 Aug 2026 19:47:35 +0000
I have the exact same gripe, but with exceptions instead. People just throw exceptions, and don't handle them, expecting that it be magically handled somewhere else. While with error code you are forced to deal with the problem locally where you are for more likely to have context in what it is that you were trying to do and what is the best course of action to do instead.

If you have people just casting the return value just to not deal with it, I mean that just negligence from the part of the developer at that point. You did your best to warn people of the problem, and they have to get creative to do something to ignore it.
The same way you can't force people to catch your exception, except you can see it when you cast the return code away.

I would write that down to there are a lot of developers out there that probably should have considered a different line of work and don't handle errors period.
Seems cliché to blame it on a skill issue, but at some point it is. We should aim the feature for professionals, i.e. if you do your best to do things right you should be rewarded with having a functional language as opposed tailoring the language for amateurs and then no matter how good you are things will never be good because the language was broken for the sake of "simplicity".

-----Original Message-----
From: Std-Proposals <std-proposals-bounces_at_lists.isocpp.org> On Behalf Of Henry Miller via Std-Proposals
Sent: Saturday, August 29, 2026 13:43
To: Henry Miller via Std-Proposals <std-proposals_at_lists.isocpp.org>
Cc: Henry Miller <hank_at_[hidden]>
Subject: Re: [std-proposals] fine-control exception/error/UB handling on function boundary



On Sat, Aug 29, 2026, at 03:42, Tiago Freire via Std-Proposals wrote:
> This is exactly the trap I've seen in production code over and over again.
>
> From my experience code that uses exceptions is more unstable
> (unexpectedly crashes), and 9 times out of 10 the reason for that
> instability is unhandled exceptions.
> You throw a new exception and you think you don't have to care, and
> that it somehow it all gets solved by magic. But somewhere up the call
> stack someone has to, and it is really hard to find out where that is.
> Every time you throw a new type of exception you incur a debt that has
> to be paid.

This doesn't surprise me. However, I still question it. I mean, sure, if you don't use exceptions, you're not going to crash because of an unhandled exception, but that doesn't mean your program is correct for not using exceptions.

My experience is with programs that don't use exceptions, and the general rule, I would say, is people are just ignoring errors. That means when things go wrong, there is no clue that anyone has. Something went wrong, but we have no clue a customer calls the tech support line and support has to guess because there is no idea anyone has. they could open a bug But we have no idea where in the code something went wrong, thus it is really hard to fix. These are error situations, so we can do thousands and thousands of hours of testing manually. We can throw them at automatic test frameworks, but the reality is they are unlikely to occur, and so a user is likely to be the first that sees it

And there is a real problem. People are not handling errors. And I don't know how to make them. I have used error codes in the past, and even when I add [[modular]]] on the result of my function, I just find people casting the return value to avoid rather than handling it. If anyone has any idea what a language can do to solve this problem, please let me know. For now, I am stuck like most of us. People are not handling errors unless they occur so often that they're forced to.


> You can always argue "I don't have to care here, I don't have to care
> there" and you can squint your eyes and say "yeah...ok", but then make
> the mistake of thinking that you "don't have to care anywhere".
> That's how bugs of this sort often get introduced. This is precisely
> the kind of thing that should pop up in a code review and get the
> comment "hey can you not introduce a new exception and just use
> std::exception [like always]?" or "have you validated that all code
> upstream is prepared to receive this exception?"
>
>
> -----Original Message-----
> From: Marcin Jaczewski <marcinjaczewski86_at_gmail.com>
> Sent: Saturday, August 29, 2026 00:33
> To: Tiago Freire <tmiguelf_at_hotmail.com>
> Cc: marcinjaczewski86_at_gmail.com; std-proposals_at_lists.isocpp.org; Henry
> Miller <hank_at_millerfarm.com>; Zamfir Yonchev
> <zamfir.yonchev_at_gmail.com>
> Subject: Re: [std-proposals] fine-control exception/error/UB handling
> on function boundary
>
> pt., 28 sie 2026 o 20:52 Tiago Freire <tmiguelf_at_hotmail.com> napisał(a):
>>
>>
>> > If a function already throws an exception, does this matter what new type it throws for the whole call stack?
>> > Only place where it matters is the place where you handle this new exception, rest code should stay agnostic to this new error case.
>>
>> Yes, it does matter. How else are you going to find out the "Only place[s] where you handle this new exception"?
>> That's a much harder problem with exceptions than just looking up who is calling your function.
>
> In both cases you need to scan whole call tree in both cases anyway,
> and in case of error codes you need to modify lot of more code to make
> it work.
>
> Consider simple function:
>
> ```
> void foo(auto& data, auto& callback)
> {
> for (auto& d : data)
> {
> callback(d);
> }
> }
> ```
>
> Its already used in contexts where callback can break this loop by
> throwing. Will adding 10 new exceptions there change anything?
> Code could be fully agnostic to exception handling.
>
> Lest see another function:
>
> ```
> auto bar(auto& callback)
> {
> int i = 0;
> while (true)
> {
> try { return callback(); } catch (Timeout&) { if (++i > 10)
> throw; Wait(2); }
> }
> }
> ```
>
> Does this function need to know about any new exceptions? No unless is
> equivalent to `Timeout` and there is a point to repeat operation.
> If this is the case then that function is the place where we handle
> new expeditions otherwise we can ignore it.
>
> In return codes you need to update whole call stack to propagate a new
> error type.
>
>>
>> > Error codes are the same
>>
>> They most certainly are not. The day you find a function were adding an error code silently adds a new return type to the caller without so much as a warning, is the day I will quit programming.
>>
>
> We have opposite case, change `void Baz()` into `Error Baz()` compiles
> in majority cases without any errors.
> You need to add attributes to prevent this.
>
>> > Error codes are the same, if you need to add failable operation to any function you need to add propagation of failure upstream.
>>
>> Do you?
>>
>> > If the function returns `void` you need to change it to return some error code.
>>
>> Yes!
>> And the problem is?
>> You changed the contract. Something that used to not fail, now does. If the code was not prepared to handle that, it should fail to compile and force you to fix the caller.
>> I don't see "having to do more work to fix other places because the interface has changed" as a problem, the problem is having to do all that and not know, and bugs are created and left to rot your code base.
>> That's why I think it would be amazing in code bases that used exceptions if functions were forced to document what exceptions they could throw. You would "catch" the problem immediately instead of staying undiscovered until one day they come to bite you.
>>
>
> In most cases it only matters if it throws or not, what is thrown is a
> secondary concern. This is why C++ added `noexcept`.
> Probably some new attributes that work similar to one that prevent
> ignoring return values could be added to enforce adding
> `noexcept(true)` and `noexcept(false)` to more functions.
>
>>
>> > Even better if you have robust error codes of 20 diffrent values and add some C code that has its own error codes how do you map both? Do you glue them together?
>>
>> Are you making an argument for return codes? If function X returns an error code in the standard A, but it calls function Y that returns an error code in standard B, if Y adds a new error to B, and X wants to propagate that extra detail to the caller then it must have to convert it to standard A, so that the caller of X only ever has to deal with errors in standard A.
>> Exceptions is where you have to deal with the mess of having to deal with errors in standard A and standard B, or whatever C D or E may occur in the future, which you have no way to know because the interface doesn't document that.
>>
>> You wouldn't do this:
>> ```expected_variant<int, ErrorA, ErrorB, ErrorC> Foo(); ```
>>
>> You just do this:
>> ```expected_variant<int, ErrorA> Foo(); ``` ever
>>
>> But
>> ```int Foo() throw(ErrorA, ErrorB, ErrorC); ``` Is what you get with
>> exceptions, because the caller doesn't have to deal with the exceptions.
>
> If you can easily "collapse" error codes into one then even easier you
> can do the same for exceptions.
> And catch all libs exceptions and convert them into yours at first
> ocasion. And at end of your function have:
> ```
> catch(...)
> {
> throw UnexpectedException("From function foo"); } ``` And you can
> make some reasonable handling of this case in the rest of the call
> stack.
> Most cases simply ignore it and allow it to bubble up to the top
> function where you report to the user or cancel the whole operation.
>
>> But it is made much worse if you do this:
>> ```int Foo(); ```
>> You have to catch ErrorA, ErrorB, and ErrorC, but it doesn't tell you.
>>
>> Do you see the problem?
>>
>
> This is more of a tooling problem than langage problem. As I said
> before you can create a tool that will say if you handle all
> exceptions thrown by function and you don't even need source code to
> do this as binary needs to have this information available for unwinding.
> Some new attributes that at link time check this could be useful.
>
>
>>
>> -----Original Message-----
>> From: Marcin Jaczewski <marcinjaczewski86_at_gmail.com>
>> Sent: Friday, August 28, 2026 20:02
>> To: std-proposals_at_[hidden]
>> Cc: Henry Miller <hank_at_millerfarm.com>; Tiago Freire
>> <tmiguelf_at_hotmail.com>; Zamfir Yonchev <zamfir.yonchev_at_gmail.com>
>> Subject: Re: [std-proposals] fine-control exception/error/UB handling
>> on function boundary
>>
>> pt., 28 sie 2026 o 18:33 Tiago Freire via Std-Proposals <std-proposals_at_lists.isocpp.org> napisał(a):
>> >
>> > > the real problem is as soon as you make that change you have often thousands of functions that cascading need to change because now you changed your interface.
>> >
>> > Yes, but isn't that just the symptom of a much larger problem.
>> > You haven't as much provided a useful function as you have provided a means for your application to ungracefully terminate if you don't document that your code can throw, what it can throw and how you can recover if a function does throw. I.e. you just gave everyone a loaded foot gun.
>> >
>> > If you state that your function can throw A, B, and C I can write a catch clause to catch A, B, and C and *handle the error*.
>> > But now you if you make a change and it can also throw D, the fact that this cascades trough out your entire code base is a good thing.
>> > You have changed the contract of what the function can do, code that was previously safe now isn't, and if the caller doesn't catch it you have also changed the caller's contract, then the caller of the caller will throw an additional exception as well.
>> >
>>
>> If a function already throws an exception, does this matter what new type it throws for the whole call stack?
>> Only place where it matters is the place where you handle this new exception, rest code should stay agnostic to this new error case.
>>
>> > Exceptions are evil because it changes code upstream instead of downstream.
>> >
>>
>> Error codes are the same, if you need to add failable operation to any function you need to add propagation of failure upstream.
>> If the function returns `void` you need to change it to return some error code. If it's already return some error code you need extend it to add new value and handle it upstream. Even better if you have robust error codes of 20 diffrent values and add some C code that has its own error codes how do you map both? Do you glue them together? Even is only place where this can happen?
>>
>> Only way to handle it generally is code like:
>>
>> ```
>> expected_variant<int, ErrorA, ErrorB, ErrorC> Foo(); ```
>>
>> That have exactly same problems as
>>
>> ```
>> int Foo() throw(ErrorA, ErrorB, ErrorC); ```
>>
>>
>>
>> > The fact that you have to deal with changing code in a thousand different places throughout the code base, is a small mercy that allows you the opportunity to at least validate if code upstream was able to handle it.
>> > Make that change silent and you have not fixed the problem, you just hid it and don't realize you created a problem, now you have thousands of different bugs with a single code change.
>> >
>> > That's why I'm in the camp "No exceptions" please!
>> >
>> > -----Original Message-----
>> > From: Henry Miller <hank_at_millerfarm.com>
>> > Sent: Friday, August 28, 2026 14:21
>> > To: Henry Miller via Std-Proposals <std-proposals_at_lists.isocpp.org>
>> > Cc: Tiago Freire <tmiguelf_at_hotmail.com>; Zamfir Yonchev
>> > <zamfir.yonchev_at_[hidden]>
>> > Subject: Re: [std-proposals] fine-control exception/error/UB
>> > handling on function boundary
>> >
>> >
>> > On Fri, Aug 28, 2026, at 03:38, Tiago Freire via Std-Proposals wrote:
>> >
>> > > And I would kill to be able to specify a list of possible
>> > > exceptions at the interference level to be able to validate the
>> > > code is exception safe.
>> > >
>> >
>> > A number of languages have tried that in the past and books have always quickly come up and said, never specify the exact exceptions except on a very generic level. That is, you say, 'throws anything' in whatever the language syntax specifies that as. Too many times you want to make a slight change to implementation and discover that it would really be helpful to throw a different exception in some situation. Often some users are demanding a specific bit of information that they would catch and handle differently. the real problem is as soon as you make that change you have often thousands of functions that cascading need to change because now you changed your interface. You can throw a clang tidy at the problem but that's not going to tell you the place where you should catch the exception in cases where you don't want to cascade it all the way and so it's a question of valuable even with modern tools.
>> >
>> > Now in C++ it would be useful to say throws std::exception, that is I won't throw an int or any other type that is not derived from standard exception. This is good practice anyway. It might be useful to also say, we'll not throw std::bad_alloc or something like that, where you can say a whole class of things would not be thrown.
>> >
>> > Though I'm not sure if there's any advantage to putting the above into the language versus putting it into something like the doxygen comments. I'm not sure if the compilers can use it and so long as the information of what your wheeler will throw is in place, static analysis can find it, that's probably good enough for your needs.
>> >
>> > however specific list of exceptions that a function will throw is
>> > always going to be listed in every intelligent style guide we will
>> > not use this
>> > --
>> > Std-Proposals mailing list
>> > Std-Proposals_at_lists.isocpp.org
>> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
--
Std-Proposals mailing list
Std-Proposals_at_[hidden]rg
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2026-08-29 19:47:44