Source file
src/net/error_test.go
1
2
3
4
5
6
7 package net
8
9 import (
10 "context"
11 "errors"
12 "fmt"
13 "internal/poll"
14 "io"
15 "io/fs"
16 "net/internal/socktest"
17 "os"
18 "runtime"
19 "strings"
20 "testing"
21 "time"
22 )
23
24 func (e *OpError) isValid() error {
25 if e.Op == "" {
26 return fmt.Errorf("OpError.Op is empty: %v", e)
27 }
28 if e.Net == "" {
29 return fmt.Errorf("OpError.Net is empty: %v", e)
30 }
31 for _, addr := range []Addr{e.Source, e.Addr} {
32 switch addr := addr.(type) {
33 case nil:
34 case *TCPAddr:
35 if addr == nil {
36 return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
37 }
38 case *UDPAddr:
39 if addr == nil {
40 return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
41 }
42 case *IPAddr:
43 if addr == nil {
44 return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
45 }
46 case *IPNet:
47 if addr == nil {
48 return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
49 }
50 case *UnixAddr:
51 if addr == nil {
52 return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
53 }
54 case *pipeAddr:
55 if addr == nil {
56 return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
57 }
58 case fileAddr:
59 if addr == "" {
60 return fmt.Errorf("OpError.Source or Addr is empty: %#v, %v", addr, e)
61 }
62 default:
63 return fmt.Errorf("OpError.Source or Addr is unknown type: %T, %v", addr, e)
64 }
65 }
66 if e.Err == nil {
67 return fmt.Errorf("OpError.Err is empty: %v", e)
68 }
69 return nil
70 }
71
72
73
74
75 func parseDialError(nestedErr error) error {
76 if nestedErr == nil {
77 return nil
78 }
79
80 switch err := nestedErr.(type) {
81 case *OpError:
82 if err := err.isValid(); err != nil {
83 return err
84 }
85 nestedErr = err.Err
86 goto second
87 }
88 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
89
90 second:
91 if isPlatformError(nestedErr) {
92 return nil
93 }
94 switch err := nestedErr.(type) {
95 case *AddrError, addrinfoErrno, *timeoutError, *DNSError, InvalidAddrError, *ParseError, *poll.DeadlineExceededError, UnknownNetworkError:
96 return nil
97 case *os.SyscallError:
98 nestedErr = err.Err
99 goto third
100 case *fs.PathError:
101 nestedErr = err.Err
102 goto third
103 }
104 switch nestedErr {
105 case errCanceled, ErrClosed, errMissingAddress, errNoSuitableAddress,
106 context.DeadlineExceeded, context.Canceled:
107 return nil
108 }
109 return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
110
111 third:
112 if isPlatformError(nestedErr) {
113 return nil
114 }
115 return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
116 }
117
118 var dialErrorTests = []struct {
119 network, address string
120 }{
121 {"foo", ""},
122 {"bar", "baz"},
123 {"datakit", "mh/astro/r70"},
124 {"tcp", ""},
125 {"tcp", "127.0.0.1:☺"},
126 {"tcp", "no-such-name:80"},
127 {"tcp", "mh/astro/r70:http"},
128
129 {"tcp", JoinHostPort("127.0.0.1", "-1")},
130 {"tcp", JoinHostPort("127.0.0.1", "123456789")},
131 {"udp", JoinHostPort("127.0.0.1", "-1")},
132 {"udp", JoinHostPort("127.0.0.1", "123456789")},
133 {"ip:icmp", "127.0.0.1"},
134
135 {"unix", "/path/to/somewhere"},
136 {"unixgram", "/path/to/somewhere"},
137 {"unixpacket", "/path/to/somewhere"},
138 }
139
140 func TestDialError(t *testing.T) {
141 switch runtime.GOOS {
142 case "plan9":
143 t.Skipf("%s does not have full support of socktest", runtime.GOOS)
144 }
145
146 origTestHookLookupIP := testHookLookupIP
147 defer func() { testHookLookupIP = origTestHookLookupIP }()
148 testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
149 return nil, &DNSError{Err: "dial error test", Name: "name", Server: "server", IsTimeout: true}
150 }
151 sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
152 return nil, errOpNotSupported
153 })
154 defer sw.Set(socktest.FilterConnect, nil)
155
156 d := Dialer{Timeout: someTimeout}
157 for i, tt := range dialErrorTests {
158 c, err := d.Dial(tt.network, tt.address)
159 if err == nil {
160 t.Errorf("#%d: should fail; %s:%s->%s", i, c.LocalAddr().Network(), c.LocalAddr(), c.RemoteAddr())
161 c.Close()
162 continue
163 }
164 if tt.network == "tcp" || tt.network == "udp" {
165 nerr := err
166 if op, ok := nerr.(*OpError); ok {
167 nerr = op.Err
168 }
169 if sys, ok := nerr.(*os.SyscallError); ok {
170 nerr = sys.Err
171 }
172 if nerr == errOpNotSupported {
173 t.Errorf("#%d: should fail without %v; %s:%s->", i, nerr, tt.network, tt.address)
174 continue
175 }
176 }
177 if c != nil {
178 t.Errorf("Dial returned non-nil interface %T(%v) with err != nil", c, c)
179 }
180 if err = parseDialError(err); err != nil {
181 t.Errorf("#%d: %v", i, err)
182 continue
183 }
184 }
185 }
186
187 func TestProtocolDialError(t *testing.T) {
188 switch runtime.GOOS {
189 case "solaris", "illumos":
190 t.Skipf("not supported on %s", runtime.GOOS)
191 }
192
193 for _, network := range []string{"tcp", "udp", "ip:4294967296", "unix", "unixpacket", "unixgram"} {
194 var err error
195 switch network {
196 case "tcp":
197 _, err = DialTCP(network, nil, &TCPAddr{Port: 1 << 16})
198 case "udp":
199 _, err = DialUDP(network, nil, &UDPAddr{Port: 1 << 16})
200 case "ip:4294967296":
201 _, err = DialIP(network, nil, nil)
202 case "unix", "unixpacket", "unixgram":
203 _, err = DialUnix(network, nil, &UnixAddr{Name: "//"})
204 }
205 if err == nil {
206 t.Errorf("%s: should fail", network)
207 continue
208 }
209 if err = parseDialError(err); err != nil {
210 t.Errorf("%s: %v", network, err)
211 continue
212 }
213 }
214 }
215
216 func TestDialAddrError(t *testing.T) {
217 switch runtime.GOOS {
218 case "plan9":
219 t.Skipf("not supported on %s", runtime.GOOS)
220 }
221 if !supportsIPv4() || !supportsIPv6() {
222 t.Skip("both IPv4 and IPv6 are required")
223 }
224
225 for _, tt := range []struct {
226 network string
227 lit string
228 addr *TCPAddr
229 }{
230 {"tcp4", "::1", nil},
231 {"tcp4", "", &TCPAddr{IP: IPv6loopback}},
232
233
234
235 {"tcp6", "", &TCPAddr{IP: IP{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}}},
236 } {
237 var err error
238 var c Conn
239 var op string
240 if tt.lit != "" {
241 c, err = Dial(tt.network, JoinHostPort(tt.lit, "0"))
242 op = fmt.Sprintf("Dial(%q, %q)", tt.network, JoinHostPort(tt.lit, "0"))
243 } else {
244 c, err = DialTCP(tt.network, nil, tt.addr)
245 op = fmt.Sprintf("DialTCP(%q, %q)", tt.network, tt.addr)
246 }
247 if err == nil {
248 c.Close()
249 t.Errorf("%s succeeded, want error", op)
250 continue
251 }
252 if perr := parseDialError(err); perr != nil {
253 t.Errorf("%s: %v", op, perr)
254 continue
255 }
256 operr := err.(*OpError).Err
257 aerr, ok := operr.(*AddrError)
258 if !ok {
259 t.Errorf("%s: %v is %T, want *AddrError", op, err, operr)
260 continue
261 }
262 want := tt.lit
263 if tt.lit == "" {
264 want = tt.addr.IP.String()
265 }
266 if aerr.Addr != want {
267 t.Errorf("%s: %v, error Addr=%q, want %q", op, err, aerr.Addr, want)
268 }
269 }
270 }
271
272 var listenErrorTests = []struct {
273 network, address string
274 }{
275 {"foo", ""},
276 {"bar", "baz"},
277 {"datakit", "mh/astro/r70"},
278 {"tcp", "127.0.0.1:☺"},
279 {"tcp", "no-such-name:80"},
280 {"tcp", "mh/astro/r70:http"},
281
282 {"tcp", JoinHostPort("127.0.0.1", "-1")},
283 {"tcp", JoinHostPort("127.0.0.1", "123456789")},
284
285 {"unix", "/path/to/somewhere"},
286 {"unixpacket", "/path/to/somewhere"},
287 }
288
289 func TestListenError(t *testing.T) {
290 switch runtime.GOOS {
291 case "plan9":
292 t.Skipf("%s does not have full support of socktest", runtime.GOOS)
293 }
294
295 origTestHookLookupIP := testHookLookupIP
296 defer func() { testHookLookupIP = origTestHookLookupIP }()
297 testHookLookupIP = func(_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
298 return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
299 }
300 sw.Set(socktest.FilterListen, func(so *socktest.Status) (socktest.AfterFilter, error) {
301 return nil, errOpNotSupported
302 })
303 defer sw.Set(socktest.FilterListen, nil)
304
305 for i, tt := range listenErrorTests {
306 ln, err := Listen(tt.network, tt.address)
307 if err == nil {
308 t.Errorf("#%d: should fail; %s:%s->", i, ln.Addr().Network(), ln.Addr())
309 ln.Close()
310 continue
311 }
312 if tt.network == "tcp" {
313 nerr := err
314 if op, ok := nerr.(*OpError); ok {
315 nerr = op.Err
316 }
317 if sys, ok := nerr.(*os.SyscallError); ok {
318 nerr = sys.Err
319 }
320 if nerr == errOpNotSupported {
321 t.Errorf("#%d: should fail without %v; %s:%s->", i, nerr, tt.network, tt.address)
322 continue
323 }
324 }
325 if ln != nil {
326 t.Errorf("Listen returned non-nil interface %T(%v) with err != nil", ln, ln)
327 }
328 if err = parseDialError(err); err != nil {
329 t.Errorf("#%d: %v", i, err)
330 continue
331 }
332 }
333 }
334
335 var listenPacketErrorTests = []struct {
336 network, address string
337 }{
338 {"foo", ""},
339 {"bar", "baz"},
340 {"datakit", "mh/astro/r70"},
341 {"udp", "127.0.0.1:☺"},
342 {"udp", "no-such-name:80"},
343 {"udp", "mh/astro/r70:http"},
344
345 {"udp", JoinHostPort("127.0.0.1", "-1")},
346 {"udp", JoinHostPort("127.0.0.1", "123456789")},
347 }
348
349 func TestListenPacketError(t *testing.T) {
350 switch runtime.GOOS {
351 case "plan9":
352 t.Skipf("%s does not have full support of socktest", runtime.GOOS)
353 }
354
355 origTestHookLookupIP := testHookLookupIP
356 defer func() { testHookLookupIP = origTestHookLookupIP }()
357 testHookLookupIP = func(_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
358 return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
359 }
360
361 for i, tt := range listenPacketErrorTests {
362 c, err := ListenPacket(tt.network, tt.address)
363 if err == nil {
364 t.Errorf("#%d: should fail; %s:%s->", i, c.LocalAddr().Network(), c.LocalAddr())
365 c.Close()
366 continue
367 }
368 if c != nil {
369 t.Errorf("ListenPacket returned non-nil interface %T(%v) with err != nil", c, c)
370 }
371 if err = parseDialError(err); err != nil {
372 t.Errorf("#%d: %v", i, err)
373 continue
374 }
375 }
376 }
377
378 func TestProtocolListenError(t *testing.T) {
379 switch runtime.GOOS {
380 case "plan9":
381 t.Skipf("not supported on %s", runtime.GOOS)
382 }
383
384 for _, network := range []string{"tcp", "udp", "ip:4294967296", "unix", "unixpacket", "unixgram"} {
385 var err error
386 switch network {
387 case "tcp":
388 _, err = ListenTCP(network, &TCPAddr{Port: 1 << 16})
389 case "udp":
390 _, err = ListenUDP(network, &UDPAddr{Port: 1 << 16})
391 case "ip:4294967296":
392 _, err = ListenIP(network, nil)
393 case "unix", "unixpacket":
394 _, err = ListenUnix(network, &UnixAddr{Name: "//"})
395 case "unixgram":
396 _, err = ListenUnixgram(network, &UnixAddr{Name: "//"})
397 }
398 if err == nil {
399 t.Errorf("%s: should fail", network)
400 continue
401 }
402 if err = parseDialError(err); err != nil {
403 t.Errorf("%s: %v", network, err)
404 continue
405 }
406 }
407 }
408
409
410
411
412 func parseReadError(nestedErr error) error {
413 if nestedErr == nil {
414 return nil
415 }
416
417 switch err := nestedErr.(type) {
418 case *OpError:
419 if err := err.isValid(); err != nil {
420 return err
421 }
422 nestedErr = err.Err
423 goto second
424 }
425 if nestedErr == io.EOF {
426 return nil
427 }
428 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
429
430 second:
431 if isPlatformError(nestedErr) {
432 return nil
433 }
434 switch err := nestedErr.(type) {
435 case *os.SyscallError:
436 nestedErr = err.Err
437 goto third
438 }
439 switch nestedErr {
440 case ErrClosed, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
441 return nil
442 }
443 return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
444
445 third:
446 if isPlatformError(nestedErr) {
447 return nil
448 }
449 return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
450 }
451
452
453
454
455 func parseWriteError(nestedErr error) error {
456 if nestedErr == nil {
457 return nil
458 }
459
460 switch err := nestedErr.(type) {
461 case *OpError:
462 if err := err.isValid(); err != nil {
463 return err
464 }
465 nestedErr = err.Err
466 goto second
467 }
468 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
469
470 second:
471 if isPlatformError(nestedErr) {
472 return nil
473 }
474 switch err := nestedErr.(type) {
475 case *AddrError, addrinfoErrno, *timeoutError, *DNSError, InvalidAddrError, *ParseError, *poll.DeadlineExceededError, UnknownNetworkError:
476 return nil
477 case *os.SyscallError:
478 nestedErr = err.Err
479 goto third
480 }
481 switch nestedErr {
482 case errCanceled, ErrClosed, errMissingAddress, errTimeout, os.ErrDeadlineExceeded, ErrWriteToConnected, io.ErrUnexpectedEOF:
483 return nil
484 }
485 return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
486
487 third:
488 if isPlatformError(nestedErr) {
489 return nil
490 }
491 return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
492 }
493
494
495
496
497 func parseCloseError(nestedErr error, isShutdown bool) error {
498 if nestedErr == nil {
499 return nil
500 }
501
502
503
504
505
506
507 want := "use of closed network connection"
508 if !isShutdown && !strings.Contains(nestedErr.Error(), want) {
509 return fmt.Errorf("error string %q does not contain expected string %q", nestedErr, want)
510 }
511
512 if !isShutdown && !errors.Is(nestedErr, ErrClosed) {
513 return fmt.Errorf("errors.Is(%v, errClosed) returns false, want true", nestedErr)
514 }
515
516 switch err := nestedErr.(type) {
517 case *OpError:
518 if err := err.isValid(); err != nil {
519 return err
520 }
521 nestedErr = err.Err
522 goto second
523 }
524 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
525
526 second:
527 if isPlatformError(nestedErr) {
528 return nil
529 }
530 switch err := nestedErr.(type) {
531 case *os.SyscallError:
532 nestedErr = err.Err
533 goto third
534 case *fs.PathError:
535 nestedErr = err.Err
536 goto third
537 }
538 switch nestedErr {
539 case ErrClosed:
540 return nil
541 }
542 return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
543
544 third:
545 if isPlatformError(nestedErr) {
546 return nil
547 }
548 switch nestedErr {
549 case fs.ErrClosed:
550 return nil
551 }
552 return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
553 }
554
555 func TestCloseError(t *testing.T) {
556 ln := newLocalListener(t, "tcp")
557 defer ln.Close()
558 c, err := Dial(ln.Addr().Network(), ln.Addr().String())
559 if err != nil {
560 t.Fatal(err)
561 }
562 defer c.Close()
563
564 for i := 0; i < 3; i++ {
565 err = c.(*TCPConn).CloseRead()
566 if perr := parseCloseError(err, true); perr != nil {
567 t.Errorf("#%d: %v", i, perr)
568 }
569 }
570 for i := 0; i < 3; i++ {
571 err = c.(*TCPConn).CloseWrite()
572 if perr := parseCloseError(err, true); perr != nil {
573 t.Errorf("#%d: %v", i, perr)
574 }
575 }
576 for i := 0; i < 3; i++ {
577 err = c.Close()
578 if perr := parseCloseError(err, false); perr != nil {
579 t.Errorf("#%d: %v", i, perr)
580 }
581 err = ln.Close()
582 if perr := parseCloseError(err, false); perr != nil {
583 t.Errorf("#%d: %v", i, perr)
584 }
585 }
586
587 pc, err := ListenPacket("udp", "127.0.0.1:0")
588 if err != nil {
589 t.Fatal(err)
590 }
591 defer pc.Close()
592
593 for i := 0; i < 3; i++ {
594 err = pc.Close()
595 if perr := parseCloseError(err, false); perr != nil {
596 t.Errorf("#%d: %v", i, perr)
597 }
598 }
599 }
600
601
602
603
604 func parseAcceptError(nestedErr error) error {
605 if nestedErr == nil {
606 return nil
607 }
608
609 switch err := nestedErr.(type) {
610 case *OpError:
611 if err := err.isValid(); err != nil {
612 return err
613 }
614 nestedErr = err.Err
615 goto second
616 }
617 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
618
619 second:
620 if isPlatformError(nestedErr) {
621 return nil
622 }
623 switch err := nestedErr.(type) {
624 case *os.SyscallError:
625 nestedErr = err.Err
626 goto third
627 case *fs.PathError:
628 nestedErr = err.Err
629 goto third
630 }
631 switch nestedErr {
632 case ErrClosed, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
633 return nil
634 }
635 return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
636
637 third:
638 if isPlatformError(nestedErr) {
639 return nil
640 }
641 return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
642 }
643
644 func TestAcceptError(t *testing.T) {
645 handler := func(ls *localServer, ln Listener) {
646 for {
647 ln.(*TCPListener).SetDeadline(time.Now().Add(5 * time.Millisecond))
648 c, err := ln.Accept()
649 if perr := parseAcceptError(err); perr != nil {
650 t.Error(perr)
651 }
652 if err != nil {
653 if c != nil {
654 t.Errorf("Accept returned non-nil interface %T(%v) with err != nil", c, c)
655 }
656 if nerr, ok := err.(Error); !ok || (!nerr.Timeout() && !nerr.Temporary()) {
657 return
658 }
659 continue
660 }
661 c.Close()
662 }
663 }
664 ls := newLocalServer(t, "tcp")
665 if err := ls.buildup(handler); err != nil {
666 ls.teardown()
667 t.Fatal(err)
668 }
669
670 time.Sleep(100 * time.Millisecond)
671 ls.teardown()
672 }
673
674
675
676
677 func parseCommonError(nestedErr error) error {
678 if nestedErr == nil {
679 return nil
680 }
681
682 switch err := nestedErr.(type) {
683 case *OpError:
684 if err := err.isValid(); err != nil {
685 return err
686 }
687 nestedErr = err.Err
688 goto second
689 }
690 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
691
692 second:
693 if isPlatformError(nestedErr) {
694 return nil
695 }
696 switch err := nestedErr.(type) {
697 case *os.SyscallError:
698 nestedErr = err.Err
699 goto third
700 case *os.LinkError:
701 nestedErr = err.Err
702 goto third
703 case *fs.PathError:
704 nestedErr = err.Err
705 goto third
706 }
707 switch nestedErr {
708 case ErrClosed:
709 return nil
710 }
711 return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
712
713 third:
714 if isPlatformError(nestedErr) {
715 return nil
716 }
717 return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
718 }
719
720 func TestFileError(t *testing.T) {
721 switch runtime.GOOS {
722 case "windows":
723 t.Skipf("not supported on %s", runtime.GOOS)
724 }
725
726 f, err := os.CreateTemp("", "go-nettest")
727 if err != nil {
728 t.Fatal(err)
729 }
730 defer os.Remove(f.Name())
731 defer f.Close()
732
733 c, err := FileConn(f)
734 if err != nil {
735 if c != nil {
736 t.Errorf("FileConn returned non-nil interface %T(%v) with err != nil", c, c)
737 }
738 if perr := parseCommonError(err); perr != nil {
739 t.Error(perr)
740 }
741 } else {
742 c.Close()
743 t.Error("should fail")
744 }
745 ln, err := FileListener(f)
746 if err != nil {
747 if ln != nil {
748 t.Errorf("FileListener returned non-nil interface %T(%v) with err != nil", ln, ln)
749 }
750 if perr := parseCommonError(err); perr != nil {
751 t.Error(perr)
752 }
753 } else {
754 ln.Close()
755 t.Error("should fail")
756 }
757 pc, err := FilePacketConn(f)
758 if err != nil {
759 if pc != nil {
760 t.Errorf("FilePacketConn returned non-nil interface %T(%v) with err != nil", pc, pc)
761 }
762 if perr := parseCommonError(err); perr != nil {
763 t.Error(perr)
764 }
765 } else {
766 pc.Close()
767 t.Error("should fail")
768 }
769
770 ln = newLocalListener(t, "tcp")
771
772 for i := 0; i < 3; i++ {
773 f, err := ln.(*TCPListener).File()
774 if err != nil {
775 if perr := parseCommonError(err); perr != nil {
776 t.Error(perr)
777 }
778 } else {
779 f.Close()
780 }
781 ln.Close()
782 }
783 }
784
785 func parseLookupPortError(nestedErr error) error {
786 if nestedErr == nil {
787 return nil
788 }
789
790 switch nestedErr.(type) {
791 case *AddrError, *DNSError:
792 return nil
793 case *fs.PathError:
794 return nil
795 }
796 return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
797 }
798
View as plain text