Then it results in an absurd amount of duplication. I regularly encounter error strings like:
error:something happened:error:something happened
Then it results in an absurd amount of duplication. I regularly encounter error strings like:
error:something happened:error:something happened
Yes, and that is desired.
Error: failed processing order: account history load failure: getUser error: context deadline exeeded
Your example shows an ideal case w/o repetition. If every layer just wraps error without inspecting, then there will be duplication in the error string.
I have never seen that. I have shipped multiple dozens of services at half a dozen companies. Big code bases. Large teams. Large volumes of calls and data. Complicated distributed systems.
I am unable to imagine a case where an error string repeated itself. On a loop, an error could repeat, but those show as a numerical count value or as separate logs.
This feels like manually written stacktraces
I’d find Error: failed processing order: context deadline exceeded just as useful and more concise.
Typically there is only one possible code path if you can identify both ends.
Not in my experience. Usually your call chain has forks. Usually the DoThing function will internally do 3 things and any one of those three things failed and you need a different error message to disambiguate. And four methods call DoThing. The 12 error paths need 12 uniquely rendered error messages. Some people say "that is just stack traces," and they are close. It is a concise stack trace with the exact context that focuses on your code under control.
If you have both the start of the call chain and the end of the call chain mapped you will get a different error response almost every time and it is usually more than enough, so say your chain is:
Do1:...Do10, which then DoX,DoY,DoZ and one of those last 3 failed.
Do you really need Do1 to Do10 to be annotated to know that DoY failed when called from Do1? I find:
Do1:DoZ failed for reason bar
Just as useful and a lot shorter than: Do1: failed:Do2:failed...Do9 failed:Do10:failed:DoZ failed for reason bar
It is effectively a stack trace stored in strings, why not just embed a proper stack trace to all your errors if that is what you want?
Your concern with having a stack trace of calls seems a hypothetical concern to me but perhaps we just work on different kinds of software. I think though you should allow that for some people annotating each error just isn't that useful, even if it is useful for you.
After a decade of writing go, I always wrap with the function name and no other content. For instance:
do c: edit b: create a: something happened
For functions called doC, editB, createA.
It’s like a stack trace and super easy to find the codepath something took.
I have a single wrap function that does this for all errors. The top level handler only prints the first two, but can print all if needed.
I have never had difficulty quickly finding the error given only the top two stack sites.
Any complaint about go boilerplate is flawed. The purpose and value is not in reducing code written, it is to make code easier to read and it achieves this goal better than any other language.
This value is compounding with coding agents.