Making it harder to swallow `error.Canceled`

I forget, since it’s now been two release cycles since Writergate, but if you were interested in why, you could check out the release notes for 0.15.0 or @andrewrk’s excellent talk entitled “Don’t Forget To Flush” from that era.

A possible rule of thumb:

Errors are about control flow, and providing a useful information for the caller to be able to take a useful action. So, rename WriteFailed to CheckWriter. Then, don’t ever return WriteFailed from a function (Io.Writer obviously exempt). Instead:

  1. If you own the writer, check the writer’s impl for the error and return that (or some other more specific error, or take some other correct action, just never return the CheckWriter).
  2. If you don’t own the writer (you were passed an Io.Writer), always catch CheckWriter and replace it with an error more specific to the writer which produced the error. eg, if you took an argument foo: *io.Writer, you would turn error.CheckWriter to error.CheckFoo.

(Also true for Reader, just didn’t want to write Reader/Writer a bunch)

This bug (and it is a bug), for example, appears to be because multiple readers are layered on top of each other, and one of those layers (I think specifically this line: https://codeberg.org/ziglang/zig/src/commit/8901bfe190e2360f934c6e4330cac5eed6c8712b/lib/std/http.zig#L546) returns a reader error which can be incorrectly “smuggled” as an error of the http level reader. As such, std.http.Reader.BodyError should have errors specific for checking the underlying transport reader/writers. So bodyErr would be correctly set to something like error.CheckConnectionStreamReader.

6 Likes