authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-27 15:31:23-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-30 22:03:14-08:00
log8a80b5464022a9fb4320e37f9dfc2aa83539ff07
tree0fd5f5d40f3e9d1f600622f1c3554e95c7129aed
parentfdf1ee973e9f3cb01f6fec9e460950622cdf92e4

std: remove error.BrokenPipe from file reads, add error.EndOfStream

and make reading file streaming allowed to return 0 byte reads. According to Microsoft documentation, on Windows it is possible to get 0-byte reads from pipes when 0-byte writes are made.

8 files changed, 82 insertions(+), 96 deletions(-)

lib/std/Io.zig+4-4
...@@ -187,7 +187,7 @@ pub const VTable = struct {...@@ -187,7 +187,7 @@ pub const VTable = struct {
187 fileWritePositional: *const fn (?*anyopaque, File, header: []const u8, data: []const []const u8, splat: usize, offset: u64) File.WritePositionalError!usize,187 fileWritePositional: *const fn (?*anyopaque, File, header: []const u8, data: []const []const u8, splat: usize, offset: u64) File.WritePositionalError!usize,
188 fileWriteFileStreaming: *const fn (?*anyopaque, File, header: []const u8, *Io.File.Reader, Io.Limit) File.Writer.WriteFileError!usize,188 fileWriteFileStreaming: *const fn (?*anyopaque, File, header: []const u8, *Io.File.Reader, Io.Limit) File.Writer.WriteFileError!usize,
189 fileWriteFilePositional: *const fn (?*anyopaque, File, header: []const u8, *Io.File.Reader, Io.Limit, offset: u64) File.WriteFilePositionalError!usize,189 fileWriteFilePositional: *const fn (?*anyopaque, File, header: []const u8, *Io.File.Reader, Io.Limit, offset: u64) File.WriteFilePositionalError!usize,
190 /// Returns 0 on end of stream.190 /// Returns 0 if reading at or past the end.
191 fileReadPositional: *const fn (?*anyopaque, File, data: []const []u8, offset: u64) File.ReadPositionalError!usize,191 fileReadPositional: *const fn (?*anyopaque, File, data: []const []u8, offset: u64) File.ReadPositionalError!usize,
192 fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void,192 fileSeekBy: *const fn (?*anyopaque, File, relative_offset: i64) File.SeekError!void,
193 fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void,193 fileSeekTo: *const fn (?*anyopaque, File, absolute_offset: u64) File.SeekError!void,
...@@ -263,18 +263,18 @@ pub const Operation = union(enum) {...@@ -263,18 +263,18 @@ pub const Operation = union(enum) {
263 status: Status(void) = .{ .unstarted = {} },263 status: Status(void) = .{ .unstarted = {} },
264 };264 };
265265
266 /// Returns 0 on end of stream.266 /// May return 0 reads which is different than `error.EndOfStream`.
267 pub const FileReadStreaming = struct {267 pub const FileReadStreaming = struct {
268 file: File,268 file: File,
269 data: []const []u8,269 data: []const []u8,
270 status: Status(Error!usize) = .{ .unstarted = {} },270 status: Status(Error!usize) = .{ .unstarted = {} },
271271
272 pub const Error = error{272 pub const Error = UnendingError || error{EndOfStream};
273 pub const UnendingError = error{
273 InputOutput,274 InputOutput,
274 SystemResources,275 SystemResources,
275 /// Trying to read a directory file descriptor as if it were a file.276 /// Trying to read a directory file descriptor as if it were a file.
276 IsDir,277 IsDir,
277 BrokenPipe,
278 ConnectionResetByPeer,278 ConnectionResetByPeer,
279 /// File was not opened with read capability.279 /// File was not opened with read capability.
280 NotOpenForReading,280 NotOpenForReading,
lib/std/Io/File.zig+3-2
...@@ -552,11 +552,13 @@ pub fn setTimestampsNow(file: File, io: Io) SetTimestampsError!void {...@@ -552,11 +552,13 @@ pub fn setTimestampsNow(file: File, io: Io) SetTimestampsError!void {
552 });552 });
553}553}
554554
555pub const ReadStreamingError = error{EndOfStream} || Reader.Error;
556
555/// Returns 0 on stream end or if `buffer` has no space available for data.557/// Returns 0 on stream end or if `buffer` has no space available for data.
556///558///
557/// See also:559/// See also:
558/// * `reader`560/// * `reader`
559pub fn readStreaming(file: File, io: Io, buffer: []const []u8) Reader.Error!usize {561pub fn readStreaming(file: File, io: Io, buffer: []const []u8) ReadStreamingError!usize {
560 var operation: Io.Operation = .{ .file_read_streaming = .{562 var operation: Io.Operation = .{ .file_read_streaming = .{
561 .file = file,563 .file = file,
562 .data = buffer,564 .data = buffer,
...@@ -570,7 +572,6 @@ pub const ReadPositionalError = error{...@@ -570,7 +572,6 @@ pub const ReadPositionalError = error{
570 SystemResources,572 SystemResources,
571 /// Trying to read a directory file descriptor as if it were a file.573 /// Trying to read a directory file descriptor as if it were a file.
572 IsDir,574 IsDir,
573 BrokenPipe,
574 /// Non-blocking has been enabled, and reading from the file descriptor575 /// Non-blocking has been enabled, and reading from the file descriptor
575 /// would block.576 /// would block.
576 WouldBlock,577 WouldBlock,
lib/std/Io/File/MultiReader.zig+5-9
...@@ -15,10 +15,9 @@ pub const Context = struct {...@@ -15,10 +15,9 @@ pub const Context = struct {
15 fr: File.Reader,15 fr: File.Reader,
16 vec: [1][]u8,16 vec: [1][]u8,
17 err: ?Error,17 err: ?Error,
18 eos: bool,
19};18};
2019
21pub const Error = Allocator.Error || File.Reader.Error || Io.ConcurrentError;20pub const Error = Allocator.Error || File.ReadStreamingError || Io.ConcurrentError;
2221
23/// Trailing:22/// Trailing:
24/// * `contexts: [len]Context`23/// * `contexts: [len]Context`
...@@ -85,7 +84,6 @@ pub fn init(mr: *MultiReader, gpa: Allocator, io: Io, streams: *Streams, files:...@@ -85,7 +84,6 @@ pub fn init(mr: *MultiReader, gpa: Allocator, io: Io, streams: *Streams, files:
85 },84 },
86 .vec = .{&.{}},85 .vec = .{&.{}},
87 .err = null,86 .err = null,
88 .eos = false,
89 };87 };
90 const operations = streams.operations();88 const operations = streams.operations();
91 const ring = streams.ring();89 const ring = streams.ring();
...@@ -198,8 +196,10 @@ fn fillUntimed(context: *Context, capacity: usize) Io.Reader.Error!void {...@@ -198,8 +196,10 @@ fn fillUntimed(context: *Context, capacity: usize) Io.Reader.Error!void {
198 },196 },
199 error.EndOfStream => |e| return e,197 error.EndOfStream => |e| return e,
200 };198 };
201 if (context.err != null) return error.ReadFailed;199 if (context.err) |err| switch (err) {
202 if (context.eos) return error.EndOfStream;200 error.EndOfStream => |e| return e,
201 else => return error.ReadFailed,
202 };
203}203}
204204
205pub const FillError = Io.Batch.WaitError || error{205pub const FillError = Io.Batch.WaitError || error{
...@@ -225,10 +225,6 @@ pub fn fill(mr: *MultiReader, unused_capacity: usize, timeout: Io.Timeout) FillE...@@ -225,10 +225,6 @@ pub fn fill(mr: *MultiReader, unused_capacity: usize, timeout: Io.Timeout) FillE
225 context.err = err;225 context.err = err;
226 continue;226 continue;
227 };227 };
228 if (n == 0) {
229 context.eos = true;
230 continue;
231 }
232 const r = &context.fr.interface;228 const r = &context.fr.interface;
233 r.end += n;229 r.end += n;
234 if (r.buffer.len - r.end < unused_capacity) {230 if (r.buffer.len - r.end < unused_capacity) {
lib/std/Io/File/Reader.zig+19-15
...@@ -26,7 +26,7 @@ size_err: ?SizeError = null,...@@ -26,7 +26,7 @@ size_err: ?SizeError = null,
26seek_err: ?SeekError = null,26seek_err: ?SeekError = null,
27interface: Io.Reader,27interface: Io.Reader,
2828
29pub const Error = Io.Operation.FileReadStreaming.Error || Io.Cancelable;29pub const Error = Io.Operation.FileReadStreaming.UnendingError || Io.Cancelable;
3030
31pub const SizeError = File.StatError || error{31pub const SizeError = File.StatError || error{
32 /// Occurs if, for example, the file handle is a network socket and therefore does not have a size.32 /// Occurs if, for example, the file handle is a network socket and therefore does not have a size.
...@@ -280,14 +280,16 @@ fn readVecStreaming(r: *Reader, data: [][]u8) Io.Reader.Error!usize {...@@ -280,14 +280,16 @@ fn readVecStreaming(r: *Reader, data: [][]u8) Io.Reader.Error!usize {
280 const dest_n, const data_size = try r.interface.writableVector(&iovecs_buffer, data);280 const dest_n, const data_size = try r.interface.writableVector(&iovecs_buffer, data);
281 const dest = iovecs_buffer[0..dest_n];281 const dest = iovecs_buffer[0..dest_n];
282 assert(dest[0].len > 0);282 assert(dest[0].len > 0);
283 const n = r.file.readStreaming(io, dest) catch |err| {283 const n = r.file.readStreaming(io, dest) catch |err| switch (err) {
284 r.err = err;284 error.EndOfStream => {
285 return error.ReadFailed;285 r.size = r.pos;
286 return error.EndOfStream;
287 },
288 else => |e| {
289 r.err = e;
290 return error.ReadFailed;
291 },
286 };292 };
287 if (n == 0) {
288 r.size = r.pos;
289 return error.EndOfStream;
290 }
291 r.pos += n;293 r.pos += n;
292 if (n > data_size) {294 if (n > data_size) {
293 r.interface.end += n - data_size;295 r.interface.end += n - data_size;
...@@ -335,14 +337,16 @@ fn discard(io_reader: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize {...@@ -335,14 +337,16 @@ fn discard(io_reader: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize {
335 const dest_n, const data_size = try r.interface.writableVector(&iovecs_buffer, &data);337 const dest_n, const data_size = try r.interface.writableVector(&iovecs_buffer, &data);
336 const dest = iovecs_buffer[0..dest_n];338 const dest = iovecs_buffer[0..dest_n];
337 assert(dest[0].len > 0);339 assert(dest[0].len > 0);
338 const n = file.readStreaming(io, dest) catch |err| {340 const n = file.readStreaming(io, dest) catch |err| switch (err) {
339 r.err = err;341 error.EndOfStream => {
340 return error.ReadFailed;342 r.size = r.pos;
343 return error.EndOfStream;
344 },
345 else => |e| {
346 r.err = e;
347 return error.ReadFailed;
348 },
341 };349 };
342 if (n == 0) {
343 r.size = r.pos;
344 return error.EndOfStream;
345 }
346 r.pos += n;350 r.pos += n;
347 if (n > data_size) {351 if (n > data_size) {
348 r.interface.end += n - data_size;352 r.interface.end += n - data_size;
lib/std/Io/Threaded.zig+35-50
...@@ -8583,14 +8583,14 @@ fn fileClose(userdata: ?*anyopaque, files: []const File) void {...@@ -8583,14 +8583,14 @@ fn fileClose(userdata: ?*anyopaque, files: []const File) void {
8583 for (files) |file| posix.close(file.handle);8583 for (files) |file| posix.close(file.handle);
8584}8584}
85858585
8586fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {8586fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: []const []u8) File.ReadStreamingError!usize {
8587 const t: *Threaded = @ptrCast(@alignCast(userdata));8587 const t: *Threaded = @ptrCast(@alignCast(userdata));
8588 _ = t;8588 _ = t;
8589 if (is_windows) return fileReadStreamingWindows(file, data);8589 if (is_windows) return fileReadStreamingWindows(file, data);
8590 return fileReadStreamingPosix(file, data);8590 return fileReadStreamingPosix(file, data);
8591}8591}
85928592
8593fn fileReadStreamingPosix(file: File, data: []const []u8) File.Reader.Error!usize {8593fn fileReadStreamingPosix(file: File, data: []const []u8) File.ReadStreamingError!usize {
8594 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;8594 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
8595 var i: usize = 0;8595 var i: usize = 0;
8596 for (data) |buf| {8596 for (data) |buf| {
...@@ -8611,28 +8611,24 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.Reader.Error!usiz...@@ -8611,28 +8611,24 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.Reader.Error!usiz
8611 switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) {8611 switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) {
8612 .SUCCESS => {8612 .SUCCESS => {
8613 syscall.finish();8613 syscall.finish();
8614 if (nread == 0) return error.EndOfStream;
8614 return nread;8615 return nread;
8615 },8616 },
8616 .INTR, .TIMEDOUT => {8617 .INTR, .TIMEDOUT => {
8617 try syscall.checkCancel();8618 try syscall.checkCancel();
8618 continue;8619 continue;
8619 },8620 },
8620 else => |e| {8621 .BADF => return syscall.fail(error.IsDir), // File operation on directory.
8621 syscall.finish();8622 .IO => return syscall.fail(error.InputOutput),
8622 switch (e) {8623 .ISDIR => return syscall.fail(error.IsDir),
8623 .INVAL => |err| return errnoBug(err),8624 .NOBUFS => return syscall.fail(error.SystemResources),
8624 .FAULT => |err| return errnoBug(err),8625 .NOMEM => return syscall.fail(error.SystemResources),
8625 .BADF => return error.IsDir, // File operation on directory.8626 .NOTCONN => return syscall.fail(error.SocketUnconnected),
8626 .IO => return error.InputOutput,8627 .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
8627 .ISDIR => return error.IsDir,8628 .NOTCAPABLE => return syscall.fail(error.AccessDenied),
8628 .NOBUFS => return error.SystemResources,8629 .INVAL => |err| return syscall.errnoBug(err),
8629 .NOMEM => return error.SystemResources,8630 .FAULT => |err| return syscall.errnoBug(err),
8630 .NOTCONN => return error.SocketUnconnected,8631 else => |err| return syscall.unexpectedErrno(err),
8631 .CONNRESET => return error.ConnectionResetByPeer,
8632 .NOTCAPABLE => return error.AccessDenied,
8633 else => |err| return posix.unexpectedErrno(err),
8634 }
8635 },
8636 }8632 }
8637 }8633 }
8638 }8634 }
...@@ -8643,36 +8639,33 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.Reader.Error!usiz...@@ -8643,36 +8639,33 @@ fn fileReadStreamingPosix(file: File, data: []const []u8) File.Reader.Error!usiz
8643 switch (posix.errno(rc)) {8639 switch (posix.errno(rc)) {
8644 .SUCCESS => {8640 .SUCCESS => {
8645 syscall.finish();8641 syscall.finish();
8642 if (rc == 0) return error.EndOfStream;
8646 return @intCast(rc);8643 return @intCast(rc);
8647 },8644 },
8648 .INTR, .TIMEDOUT => {8645 .INTR, .TIMEDOUT => {
8649 try syscall.checkCancel();8646 try syscall.checkCancel();
8650 continue;8647 continue;
8651 },8648 },
8652 else => |e| {8649 .BADF => {
8653 syscall.finish();8650 syscall.finish();
8654 switch (e) {8651 if (native_os == .wasi) return error.IsDir; // File operation on directory.
8655 .INVAL => |err| return errnoBug(err),8652 return error.NotOpenForReading;
8656 .FAULT => |err| return errnoBug(err),
8657 .AGAIN => return error.WouldBlock,
8658 .BADF => {
8659 if (native_os == .wasi) return error.IsDir; // File operation on directory.
8660 return error.NotOpenForReading;
8661 },
8662 .IO => return error.InputOutput,
8663 .ISDIR => return error.IsDir,
8664 .NOBUFS => return error.SystemResources,
8665 .NOMEM => return error.SystemResources,
8666 .NOTCONN => return error.SocketUnconnected,
8667 .CONNRESET => return error.ConnectionResetByPeer,
8668 else => |err| return posix.unexpectedErrno(err),
8669 }
8670 },8653 },
8654 .AGAIN => return syscall.fail(error.WouldBlock),
8655 .IO => return syscall.fail(error.InputOutput),
8656 .ISDIR => return syscall.fail(error.IsDir),
8657 .NOBUFS => return syscall.fail(error.SystemResources),
8658 .NOMEM => return syscall.fail(error.SystemResources),
8659 .NOTCONN => return syscall.fail(error.SocketUnconnected),
8660 .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
8661 .INVAL => |err| return syscall.errnoBug(err),
8662 .FAULT => |err| return syscall.errnoBug(err),
8663 else => |err| return syscall.unexpectedErrno(err),
8671 }8664 }
8672 }8665 }
8673}8666}
86748667
8675fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!usize {8668fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingError!usize {
8676 var io_status_block: windows.IO_STATUS_BLOCK = undefined;8669 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
8677 if (ntReadFile(file.handle, data, &io_status_block)) |result| switch (result) {8670 if (ntReadFile(file.handle, data, &io_status_block)) |result| switch (result) {
8678 .status => return ntReadFileResult(&io_status_block),8671 .status => return ntReadFileResult(&io_status_block),
...@@ -8707,11 +8700,9 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us...@@ -8707,11 +8700,9 @@ fn fileReadStreamingWindows(file: File, data: []const []u8) File.Reader.Error!us
87078700
8708fn ntReadFileResult(io_status_block: *windows.IO_STATUS_BLOCK) !usize {8701fn ntReadFileResult(io_status_block: *windows.IO_STATUS_BLOCK) !usize {
8709 switch (io_status_block.u.Status) {8702 switch (io_status_block.u.Status) {
8710 .SUCCESS => {8703 .SUCCESS => return io_status_block.Information,
8711 assert(io_status_block.Information != 0);8704 .END_OF_FILE => return error.EndOfStream,
8712 return io_status_block.Information;8705 .PIPE_BROKEN => return error.EndOfStream,
8713 },
8714 .END_OF_FILE, .PIPE_BROKEN => return 0,
8715 .PENDING => unreachable,8706 .PENDING => unreachable,
8716 .INVALID_DEVICE_REQUEST => return error.IsDir,8707 .INVALID_DEVICE_REQUEST => return error.IsDir,
8717 .LOCK_NOT_GRANTED => return error.LockViolation,8708 .LOCK_NOT_GRANTED => return error.LockViolation,
...@@ -8749,15 +8740,9 @@ fn ntReadFile(handle: windows.HANDLE, data: []const []u8, iosb: *windows.IO_STAT...@@ -8749,15 +8740,9 @@ fn ntReadFile(handle: windows.HANDLE, data: []const []u8, iosb: *windows.IO_STAT
8749 return .pending;8740 return .pending;
8750 },8741 },
8751 .SUCCESS => {8742 .SUCCESS => {
8752 // Only END_OF_FILE is the true end.8743 syscall.finish();
8753 if (iosb.Information == 0) {8744 iosb.u.Status = .SUCCESS;
8754 try syscall.checkCancel();8745 return .status;
8755 continue;
8756 } else {
8757 syscall.finish();
8758 iosb.u.Status = .SUCCESS;
8759 return .status;
8760 }
8761 },8746 },
8762 .CANCELLED => {8747 .CANCELLED => {
8763 try syscall.checkCancel();8748 try syscall.checkCancel();
lib/std/Progress.zig+1-2
...@@ -984,7 +984,7 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff...@@ -984,7 +984,7 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff
984 var bytes_read: usize = 0;984 var bytes_read: usize = 0;
985 while (true) {985 while (true) {
986 const n = file.readStreaming(io, &.{pipe_buf[bytes_read..]}) catch |err| switch (err) {986 const n = file.readStreaming(io, &.{pipe_buf[bytes_read..]}) catch |err| switch (err) {
987 error.WouldBlock => break,987 error.WouldBlock, error.EndOfStream => break,
988 else => |e| {988 else => |e| {
989 std.log.debug("failed to read child progress data: {t}", .{e});989 std.log.debug("failed to read child progress data: {t}", .{e});
990 main_storage.completed_count = 0;990 main_storage.completed_count = 0;
...@@ -992,7 +992,6 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff...@@ -992,7 +992,6 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff
992 continue :main_loop;992 continue :main_loop;
993 },993 },
994 };994 };
995 if (n == 0) break;
996 if (opt_saved_metadata) |m| {995 if (opt_saved_metadata) |m| {
997 if (m.remaining_read_trash_bytes > 0) {996 if (m.remaining_read_trash_bytes > 0) {
998 assert(bytes_read == 0);997 assert(bytes_read == 0);
lib/std/process/Child.zig+15-13
...@@ -176,19 +176,21 @@ pub fn collectOutput(child: *const Child, io: Io, options: CollectOutputOptions)...@@ -176,19 +176,21 @@ pub fn collectOutput(child: *const Child, io: Io, options: CollectOutputOptions)
176 while (remaining > 0) {176 while (remaining > 0) {
177 try batch.wait(io, options.timeout);177 try batch.wait(io, options.timeout);
178 while (batch.next()) |op| {178 while (batch.next()) |op| {
179 const n = try reads[op].file_read_streaming.status.result;179 const n = reads[op].file_read_streaming.status.result catch |err| switch (err) {
180 if (n == 0) {180 error.EndOfStream => {
181 remaining -= 1;181 remaining -= 1;
182 } else {182 continue;
183 lists[op].items.len += n;183 },
184 if (lists[op].items.len > @intFromEnum(limits[op])) return error.StreamTooLong;184 else => |e| return e,
185 if (options.allocator) |gpa| try lists[op].ensureUnusedCapacity(gpa, 1);185 };
186 const cap = lists[op].unusedCapacitySlice();186 lists[op].items.len += n;
187 if (cap.len == 0) return error.StreamTooLong;187 if (lists[op].items.len > @intFromEnum(limits[op])) return error.StreamTooLong;
188 vecs[op][0] = cap;188 if (options.allocator) |gpa| try lists[op].ensureUnusedCapacity(gpa, 1);
189 reads[op].file_read_streaming.status = .{ .unstarted = {} };189 const cap = lists[op].unusedCapacitySlice();
190 batch.add(op);190 if (cap.len == 0) return error.StreamTooLong;
191 }191 vecs[op][0] = cap;
192 reads[op].file_read_streaming.status = .{ .unstarted = {} };
193 batch.add(op);
192 }194 }
193 }195 }
194}196}
lib/std/zig/system.zig-1
...@@ -420,7 +420,6 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {...@@ -420,7 +420,6 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
420 error.Canceled => |e| return e,420 error.Canceled => |e| return e,
421 error.Unexpected => |e| return e,421 error.Unexpected => |e| return e,
422 error.WouldBlock => return error.Unexpected,422 error.WouldBlock => return error.Unexpected,
423 error.BrokenPipe => return error.Unexpected,
424 error.ConnectionResetByPeer => return error.Unexpected,423 error.ConnectionResetByPeer => return error.Unexpected,
425 error.NotOpenForReading => return error.Unexpected,424 error.NotOpenForReading => return error.Unexpected,
426 error.SocketUnconnected => return error.Unexpected,425 error.SocketUnconnected => return error.Unexpected,