• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

tarantool / go-tarantool / 25370127328

05 May 2026 10:05AM UTC coverage: 75.668% (+0.2%) from 75.435%
25370127328

Pull #589

github

bigbes
api: redesign errors around errors.Is and errors.As

Rename tarantool.Error to ServerError, promote the seven legacy uint32
client error codes to package-level error sentinels (ErrConnectionClosed,
ErrTimeouted, ...) and expose their numeric form as a typed
ClientErrorCode (CodeConnectionClosed, ...). ClientError gains a Cause
field plus Unwrap() []error so errors.Is matches the sentinel and
errors.As reaches the underlying net error. Retryability is expressed as
a sentinel (ErrRetryable) joined into the chain; IsRetryable wraps
errors.Is. The deprecated Temporary() method is removed.

Internal callers in connection.go and response.go switched to
newClientError / ServerError; I/O failures now wrap the original error
instead of stringifying it. Tests in box, arrow, and the root package
were migrated from typeassertions to errors.As / errors.Is.

Also fixes a pre-existing %x formatting bug on iproto.Error in
ServerError.Error().

Closes #469
Pull Request #589: api: redesign errors around errors.Is and errors.As

63 of 97 new or added lines in 4 files covered. (64.95%)

8 existing lines in 1 file now uncovered.

3144 of 4155 relevant lines covered (75.67%)

9896.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.44
/errors.go
1
package tarantool
2

3
import (
4
        "errors"
5
        "fmt"
6
        "strings"
7

8
        "github.com/tarantool/go-iproto"
9
)
10

11
// ClientErrorCode is the numeric identifier for a client-side error.
12
// Values are stable across Tarantool client implementations.
13
type ClientErrorCode uint32
14

15
// Client error codes. The numeric values match the legacy uint32
16
// constants exposed by previous versions of go-tarantool.
17
const (
18
        CodeConnectionNotReady ClientErrorCode = 0x4000 + iota
19
        CodeConnectionClosed
20
        CodeProtocolError
21
        CodeTimeouted
22
        CodeRateLimited
23
        CodeConnectionShutdown
24
        CodeIoError
25
)
26

27
// sentinelError is a package-level error value matched via errors.Is.
28
type sentinelError struct {
29
        code ClientErrorCode
30
        msg  string
31
}
32

33
func (s *sentinelError) Error() string         { return s.msg }
11✔
NEW
34
func (s *sentinelError) Code() ClientErrorCode { return s.code }
×
35

36
// Sentinel errors for client-side failure modes. Compare with errors.Is.
37
var (
38
        ErrConnectionNotReady error = &sentinelError{CodeConnectionNotReady, "connection not ready"}
39
        ErrConnectionClosed   error = &sentinelError{CodeConnectionClosed, "connection closed"}
40
        ErrProtocolError      error = &sentinelError{CodeProtocolError, "protocol error"}
41
        ErrTimeouted          error = &sentinelError{CodeTimeouted, "request timed out"}
42
        ErrRateLimited        error = &sentinelError{CodeRateLimited, "rate limited"}
43
        ErrConnectionShutdown error = &sentinelError{CodeConnectionShutdown, "connection shutdown"}
44
        ErrIoError            error = &sentinelError{CodeIoError, "I/O error"}
45

46
        // ErrRetryable marks errors that may succeed on retry. It is
47
        // joined into the error chain of any retryable ClientError so
48
        // that errors.Is(err, ErrRetryable) returns true.
49
        ErrRetryable = errors.New("retryable")
50
)
51

52
// codeToSentinel maps a code to its package-level sentinel.
53
var codeToSentinel = map[ClientErrorCode]error{
54
        CodeConnectionNotReady: ErrConnectionNotReady,
55
        CodeConnectionClosed:   ErrConnectionClosed,
56
        CodeProtocolError:      ErrProtocolError,
57
        CodeTimeouted:          ErrTimeouted,
58
        CodeRateLimited:        ErrRateLimited,
59
        CodeConnectionShutdown: ErrConnectionShutdown,
60
        CodeIoError:            ErrIoError,
61
}
62

63
// retryableSentinels lists the codes whose chain implies ErrRetryable.
64
var retryableSentinels = map[ClientErrorCode]struct{}{
65
        CodeConnectionNotReady: {},
66
        CodeTimeouted:          {},
67
        CodeRateLimited:        {},
68
        CodeIoError:            {},
69
}
70

71
// ClientError is a failure produced by this client: connection state
72
// transitions, request timeouts, protocol decoding, or I/O.
73
//
74
// Compare with package sentinels via errors.Is. If Cause is set, it
75
// is reachable via errors.As / errors.Unwrap.
76
type ClientError struct {
77
        Code  ClientErrorCode
78
        Msg   string
79
        Cause error
80
}
81

82
// Error formats as "<sentinel>: <Msg>: <cause>", omitting any empty
83
// segment. If the code has no registered sentinel, the prefix falls
84
// back to "client error 0x<code>".
85
func (e ClientError) Error() string {
12✔
86
        parts := make([]string, 0, 3)
12✔
87
        if s, ok := codeToSentinel[e.Code]; ok {
23✔
88
                parts = append(parts, s.Error())
11✔
89
        } else {
12✔
90
                parts = append(parts, fmt.Sprintf("client error 0x%x", uint32(e.Code)))
1✔
91
        }
1✔
92
        if e.Msg != "" && parts[0] != e.Msg {
23✔
93
                parts = append(parts, e.Msg)
11✔
94
        }
11✔
95
        if e.Cause != nil {
14✔
96
                parts = append(parts, e.Cause.Error())
2✔
97
        }
2✔
98
        return strings.Join(parts, ": ")
12✔
99
}
100

101
// Unwrap exposes the sentinel, ErrRetryable (when applicable), and
102
// the underlying cause to errors.Is / errors.As.
103
func (e ClientError) Unwrap() []error {
81✔
104
        out := make([]error, 0, 3)
81✔
105
        if s, ok := codeToSentinel[e.Code]; ok {
162✔
106
                out = append(out, s)
81✔
107
        }
81✔
108
        if _, ok := retryableSentinels[e.Code]; ok {
97✔
109
                out = append(out, ErrRetryable)
16✔
110
        }
16✔
111
        if e.Cause != nil {
84✔
112
                out = append(out, e.Cause)
3✔
113
        }
3✔
114
        return out
81✔
115
}
116

117
// newClientError is the internal constructor used across the package.
118
func newClientError(code ClientErrorCode, msg string, cause error) ClientError {
724✔
119
        return ClientError{Code: code, Msg: msg, Cause: cause}
724✔
120
}
724✔
121

122
// ServerError wraps an error returned by the Tarantool server.
123
type ServerError struct {
124
        Code         iproto.Error
125
        Msg          string
126
        ExtendedInfo *BoxError
127
}
128

129
// Error converts a ServerError to a string.
130
func (e ServerError) Error() string {
7✔
131
        if e.ExtendedInfo != nil {
13✔
132
                return e.ExtendedInfo.Error()
6✔
133
        }
6✔
134
        return fmt.Sprintf("%s (0x%x)", e.Msg, int(e.Code))
1✔
135
}
136

137
// IsRetryable reports whether err indicates a transient failure that
138
// may succeed on retry.
139
func IsRetryable(err error) bool {
7✔
140
        return errors.Is(err, ErrRetryable)
7✔
141
}
7✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc