It may be isomorphic to exception handling, but it promotes a meaningfully-different human style. The exception-based isomorphism to the usual returned-error handling style would look something like:
try:
a_single_statement_here()
except:
if exception.type == "A":
#yada yada
elsif exception.type == "B":
#yada yada
else:
raise
Exceptions by contrast encourage putting a lot of statements together, and much less frequently checking for what the exception may be, and capturing fewer of them. The code snippet above is bad exception style because you shouldn't be putting a try around every statement like that.
Exceptions expressed in the error handling modality probably look something more like:
val, err = func()
goto EXCEPTION if err
nextval, err = func2(val)
goto EXCEPTION if err
# etc etc, for the whole "try" block
return goodval
EXCEPTION: {
if err.type == "A" { ... }
if err.type == "B" { ... }
return err
}
which is, intriguingly, also very bad style for an error-returning language; the EXCEPTION block must be written excessively generically, when in fact the error handling may in fact care about the difference between whether the error came from func or func2, and may even want to take different actions (especially w.r.t. retrying). You can of course write those different actions into an exception block, but the style discourages that... your code starts to look messier (as nice as exceptions can be, they're syntactically pretty heavy, and also difficult to factor out the way that error-return handling can be factored out with monads and such (albeit not in Go so much)), it takes more work, so you're less inclined to do it.
It remains unclear to me what "the best" is. Error-returning code is, both in theory and in my practice, more correct, in terms of the programming style it affords; it turns out that in ways you won't see if you've only ever done exceptions-based code that your exception handlers are often incorrect, overly generic, and missing opportunities to intelligently handle bugs. The flip side is that all that extra handling is of course expensive, and it's not 100% clear that it's worth the expense, and exception code, while IMHO clearly generally less correct in both theory and practice, is equally clearly often "good enough".
I find myself wondering if some of the divisions of opinion come from different environments. In a conventional GUI program, I favor the exceptions; frankly so much stuff can go wrong that an enumeration of all possible failures is very difficult to produce, and your answer to almost all of them is likely to be identical ("scream an error to the user and hope they can fix it"). In a network server, I heavily favor the error returning paradigm; in my mature Go codebases that are network servers, I took a survey once and about 1/3rd of the lines that received an error immediately did something with them, other than simply passing them up the stack. Converting that to exceptions would actually lengthen the program and be a royal pain to deal with going forward.