Mostly, though, it was the debugging ecosystem that was the bigger issue with error handling. With the version I'm writing in PHP/Laravel, stack traces are very clear about where they happen and involving what components. When I plug in Sentry, this makes debugging production issues much easier.
Go's code is more elegant, more performant, and is fun to write, but maintaining PHP in production is much nicer and less time-consuming.
For a project like this, where it's just something I do in my spare time for fun, not having to spend a lot of time managing production is important.
if err := foo(x); err != nil {
return fmt.Errorf("foo: bad argument %s", x)
} return fmt.Errorf("ListThings failed: %w", err)
which is a different error type, but the caller can downcast it into the original filesystem error type (even across multiple layers of wrapping) if they're interested in specifically these types of errors.† https://www.techempower.com/benchmarks/#section=data-r19&hw=...
It's funny you use PHP here, as PHP's errors can be problematic in various cases cases on account of making it impossible to get some information from them (like why an fopen() call failed, for example). Never mind the whole "errors" vs. "exceptions" schism (which was improved somewhat with PHP 7, but still has various cases where it's less-than-elegant).