This is an automated email from the ASF dual-hosted git repository.

joaoreis pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra-gocql-driver.git


The following commit(s) were added to refs/heads/trunk by this push:
     new b86c662e Add dedicated error types for all generic server error codes
b86c662e is described below

commit b86c662e14e223d2c85b68c05b537f52e25ed9d6
Author: Paolo Elefante <[email protected]>
AuthorDate: Thu Mar 19 00:08:36 2026 +0100

    Add dedicated error types for all generic server error codes
    
    Introduce distinct concrete types for all server error codes that were
    previously returned as generic errorFrame values:
    
    RequestErrOverloaded, RequestErrBootstrapping, RequestErrInvalid,
    RequestErrConfig, RequestErrCredentials, RequestErrProtocol,
    RequestErrServer, RequestErrSyntax, RequestErrTruncate,
    RequestErrUnauthorized
    
    Each type embeds errorFrame and follows the existing pattern used for
    RequestErrUnavailable, RequestErrWriteTimeout, RequestErrReadTimeout.
    
    The catch-all case and the TODO comment in parseErrorFrame are removed.
    
    Add tests covering parsing of all ten error codes.
    
    Patch by Paolo Elefante; reviewed by João Reis, Bohdan Siryk for CASSGO-113
---
 CHANGELOG.md  |   1 +
 errors.go     |  40 +++++++++
 frame.go      |  20 ++++-
 frame_test.go | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 312 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dcbbca73..c064c600 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ and this project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0
 - Support for session ready, host state, topology change and schema changes 
custom listeners (CASSGO-101)
 - Add Session.AllKeyspaceMetadata() (CASSGO-109)
 - Add GetSerialConsistency method to Query and Batch (CASSGO-103)
+- Add RequestErrOverloaded, RequestErrBootstrapping, RequestErrInvalid, 
RequestErrConfig, RequestErrCredentials, RequestErrSyntax, RequestErrTruncate, 
RequestErrUnauthorized for dedicated error handling (CASSGO-113)
 
 ### Changed
 
diff --git a/errors.go b/errors.go
index 4305f78f..95aeb907 100644
--- a/errors.go
+++ b/errors.go
@@ -154,6 +154,46 @@ func (e *RequestErrUnavailable) String() string {
        return fmt.Sprintf("[request_error_unavailable consistency=%s 
required=%d alive=%d]", e.Consistency, e.Required, e.Alive)
 }
 
+// RequestErrOverloaded represents an overloaded error returned by Cassandra.
+type RequestErrOverloaded struct {
+       errorFrame
+}
+
+// RequestErrBootstrapping represents a bootstrapping error returned by 
Cassandra.
+type RequestErrBootstrapping struct {
+       errorFrame
+}
+
+// RequestErrInvalid represents an invalid query error returned by Cassandra.
+type RequestErrInvalid struct {
+       errorFrame
+}
+
+// RequestErrConfig represents a configuration error returned by Cassandra.
+type RequestErrConfig struct {
+       errorFrame
+}
+
+// RequestErrCredentials represents a credentials error returned by Cassandra.
+type RequestErrCredentials struct {
+       errorFrame
+}
+
+// RequestErrSyntax represents a syntax error returned by Cassandra.
+type RequestErrSyntax struct {
+       errorFrame
+}
+
+// RequestErrTruncate represents a truncation error returned by Cassandra.
+type RequestErrTruncate struct {
+       errorFrame
+}
+
+// RequestErrUnauthorized represents an unauthorized error returned by 
Cassandra.
+type RequestErrUnauthorized struct {
+       errorFrame
+}
+
 // ErrorMap maps node IP addresses to their respective error codes for 
read/write failure responses.
 // Each entry represents a node that failed during the operation, with the key 
being the node's
 // IP address as a string and the value being the specific error code returned 
by that node.
diff --git a/frame.go b/frame.go
index c9790b0a..7ad118c9 100644
--- a/frame.go
+++ b/frame.go
@@ -759,10 +759,24 @@ func (f *framer) parseErrorFrame() (frame, error) {
                        return nil, err
                }
                return res, nil
-       case ErrCodeInvalid, ErrCodeBootstrapping, ErrCodeConfig, 
ErrCodeCredentials, ErrCodeOverloaded,
-               ErrCodeProtocol, ErrCodeServer, ErrCodeSyntax, ErrCodeTruncate, 
ErrCodeUnauthorized:
-               // TODO(zariel): we should have some distinct types for these 
errors
+       case ErrCodeOverloaded:
+               return &RequestErrOverloaded{errorFrame: errD}, nil
+       case ErrCodeBootstrapping:
+               return &RequestErrBootstrapping{errorFrame: errD}, nil
+       case ErrCodeInvalid:
+               return &RequestErrInvalid{errorFrame: errD}, nil
+       case ErrCodeConfig:
+               return &RequestErrConfig{errorFrame: errD}, nil
+       case ErrCodeCredentials:
+               return &RequestErrCredentials{errorFrame: errD}, nil
+       case ErrCodeServer, ErrCodeProtocol:
                return errD, nil
+       case ErrCodeSyntax:
+               return &RequestErrSyntax{errorFrame: errD}, nil
+       case ErrCodeTruncate:
+               return &RequestErrTruncate{errorFrame: errD}, nil
+       case ErrCodeUnauthorized:
+               return &RequestErrUnauthorized{errorFrame: errD}, nil
        default:
                return nil, fmt.Errorf("unknown error code: 0x%x", errD.code)
        }
diff --git a/frame_test.go b/frame_test.go
index 26430beb..0bd7edad 100644
--- a/frame_test.go
+++ b/frame_test.go
@@ -893,3 +893,257 @@ func Test_newFrame_compressionFlag(t *testing.T) {
                })
        }
 }
+
+func newErrorFrameForTest(code int, msg string) *framer {
+       f := newFramer(nil, protoVersion4, GlobalTypes)
+       f.header = &frameHeader{
+               version: protoVersion4 | protoDirectionMask,
+               stream:  1,
+               op:      opError,
+       }
+       f.writeInt(int32(code))
+       f.writeString(msg)
+       return f
+}
+
+func TestParseErrorFrameDedicatedTypes(t *testing.T) {
+       tests := []struct {
+               name    string
+               code    int
+               msg     string
+               assertT func(*testing.T, frame)
+       }{
+               {
+                       name: "overloaded",
+                       code: ErrCodeOverloaded,
+                       msg:  "coordinator overloaded",
+                       assertT: func(t *testing.T, got frame) {
+                               reqErr, ok := got.(*RequestErrOverloaded)
+                               if !ok {
+                                       t.Fatalf("expected 
*RequestErrOverloaded, got %T", got)
+                               }
+                               if reqErr.Code() != ErrCodeOverloaded {
+                                       t.Fatalf("expected code %x, got %x", 
ErrCodeOverloaded, reqErr.Code())
+                               }
+                               if reqErr.Message() != "coordinator overloaded" 
{
+                                       t.Fatalf("expected message %q, got %q", 
"coordinator overloaded", reqErr.Message())
+                               }
+                               if reqErr.Error() != "coordinator overloaded" {
+                                       t.Fatalf("expected error string %q, got 
%q", "coordinator overloaded", reqErr.Error())
+                               }
+                               if reqErr.Header().op != opError {
+                                       t.Fatalf("expected op %v, got %v", 
opError, reqErr.Header().op)
+                               }
+                       },
+               },
+               {
+                       name: "bootstrapping",
+                       code: ErrCodeBootstrapping,
+                       msg:  "node is bootstrapping",
+                       assertT: func(t *testing.T, got frame) {
+                               reqErr, ok := got.(*RequestErrBootstrapping)
+                               if !ok {
+                                       t.Fatalf("expected 
*RequestErrBootstrapping, got %T", got)
+                               }
+                               if reqErr.Code() != ErrCodeBootstrapping {
+                                       t.Fatalf("expected code %x, got %x", 
ErrCodeBootstrapping, reqErr.Code())
+                               }
+                               if reqErr.Message() != "node is bootstrapping" {
+                                       t.Fatalf("expected message %q, got %q", 
"node is bootstrapping", reqErr.Message())
+                               }
+                               if reqErr.Error() != "node is bootstrapping" {
+                                       t.Fatalf("expected error string %q, got 
%q", "node is bootstrapping", reqErr.Error())
+                               }
+                               if reqErr.Header().op != opError {
+                                       t.Fatalf("expected op %v, got %v", 
opError, reqErr.Header().op)
+                               }
+                       },
+               },
+               {
+                       name: "invalid",
+                       code: ErrCodeInvalid,
+                       msg:  "invalid query",
+                       assertT: func(t *testing.T, got frame) {
+                               reqErr, ok := got.(*RequestErrInvalid)
+                               if !ok {
+                                       t.Fatalf("expected *RequestErrInvalid, 
got %T", got)
+                               }
+                               if reqErr.Code() != ErrCodeInvalid {
+                                       t.Fatalf("expected code %x, got %x", 
ErrCodeInvalid, reqErr.Code())
+                               }
+                               if reqErr.Message() != "invalid query" {
+                                       t.Fatalf("expected message %q, got %q", 
"invalid query", reqErr.Message())
+                               }
+                               if reqErr.Error() != "invalid query" {
+                                       t.Fatalf("expected error string %q, got 
%q", "invalid query", reqErr.Error())
+                               }
+                               if reqErr.Header().op != opError {
+                                       t.Fatalf("expected op %v, got %v", 
opError, reqErr.Header().op)
+                               }
+                       },
+               },
+               {
+                       name: "config",
+                       code: ErrCodeConfig,
+                       msg:  "configuration error",
+                       assertT: func(t *testing.T, got frame) {
+                               reqErr, ok := got.(*RequestErrConfig)
+                               if !ok {
+                                       t.Fatalf("expected *RequestErrConfig, 
got %T", got)
+                               }
+                               if reqErr.Code() != ErrCodeConfig {
+                                       t.Fatalf("expected code %x, got %x", 
ErrCodeConfig, reqErr.Code())
+                               }
+                               if reqErr.Message() != "configuration error" {
+                                       t.Fatalf("expected message %q, got %q", 
"configuration error", reqErr.Message())
+                               }
+                               if reqErr.Error() != "configuration error" {
+                                       t.Fatalf("expected error string %q, got 
%q", "configuration error", reqErr.Error())
+                               }
+                               if reqErr.Header().op != opError {
+                                       t.Fatalf("expected op %v, got %v", 
opError, reqErr.Header().op)
+                               }
+                       },
+               },
+               {
+                       name: "credentials",
+                       code: ErrCodeCredentials,
+                       msg:  "bad credentials",
+                       assertT: func(t *testing.T, got frame) {
+                               reqErr, ok := got.(*RequestErrCredentials)
+                               if !ok {
+                                       t.Fatalf("expected 
*RequestErrCredentials, got %T", got)
+                               }
+                               if reqErr.Code() != ErrCodeCredentials {
+                                       t.Fatalf("expected code %x, got %x", 
ErrCodeCredentials, reqErr.Code())
+                               }
+                               if reqErr.Message() != "bad credentials" {
+                                       t.Fatalf("expected message %q, got %q", 
"bad credentials", reqErr.Message())
+                               }
+                               if reqErr.Error() != "bad credentials" {
+                                       t.Fatalf("expected error string %q, got 
%q", "bad credentials", reqErr.Error())
+                               }
+                               if reqErr.Header().op != opError {
+                                       t.Fatalf("expected op %v, got %v", 
opError, reqErr.Header().op)
+                               }
+                       },
+               },
+               {
+                       name: "syntax",
+                       code: ErrCodeSyntax,
+                       msg:  "syntax error",
+                       assertT: func(t *testing.T, got frame) {
+                               reqErr, ok := got.(*RequestErrSyntax)
+                               if !ok {
+                                       t.Fatalf("expected *RequestErrSyntax, 
got %T", got)
+                               }
+                               if reqErr.Code() != ErrCodeSyntax {
+                                       t.Fatalf("expected code %x, got %x", 
ErrCodeSyntax, reqErr.Code())
+                               }
+                               if reqErr.Message() != "syntax error" {
+                                       t.Fatalf("expected message %q, got %q", 
"syntax error", reqErr.Message())
+                               }
+                               if reqErr.Error() != "syntax error" {
+                                       t.Fatalf("expected error string %q, got 
%q", "syntax error", reqErr.Error())
+                               }
+                               if reqErr.Header().op != opError {
+                                       t.Fatalf("expected op %v, got %v", 
opError, reqErr.Header().op)
+                               }
+                       },
+               },
+               {
+                       name: "truncate",
+                       code: ErrCodeTruncate,
+                       msg:  "truncation error",
+                       assertT: func(t *testing.T, got frame) {
+                               reqErr, ok := got.(*RequestErrTruncate)
+                               if !ok {
+                                       t.Fatalf("expected *RequestErrTruncate, 
got %T", got)
+                               }
+                               if reqErr.Code() != ErrCodeTruncate {
+                                       t.Fatalf("expected code %x, got %x", 
ErrCodeTruncate, reqErr.Code())
+                               }
+                               if reqErr.Message() != "truncation error" {
+                                       t.Fatalf("expected message %q, got %q", 
"truncation error", reqErr.Message())
+                               }
+                               if reqErr.Error() != "truncation error" {
+                                       t.Fatalf("expected error string %q, got 
%q", "truncation error", reqErr.Error())
+                               }
+                               if reqErr.Header().op != opError {
+                                       t.Fatalf("expected op %v, got %v", 
opError, reqErr.Header().op)
+                               }
+                       },
+               },
+               {
+                       name: "unauthorized",
+                       code: ErrCodeUnauthorized,
+                       msg:  "unauthorized",
+                       assertT: func(t *testing.T, got frame) {
+                               reqErr, ok := got.(*RequestErrUnauthorized)
+                               if !ok {
+                                       t.Fatalf("expected 
*RequestErrUnauthorized, got %T", got)
+                               }
+                               if reqErr.Code() != ErrCodeUnauthorized {
+                                       t.Fatalf("expected code %x, got %x", 
ErrCodeUnauthorized, reqErr.Code())
+                               }
+                               if reqErr.Message() != "unauthorized" {
+                                       t.Fatalf("expected message %q, got %q", 
"unauthorized", reqErr.Message())
+                               }
+                               if reqErr.Error() != "unauthorized" {
+                                       t.Fatalf("expected error string %q, got 
%q", "unauthorized", reqErr.Error())
+                               }
+                               if reqErr.Header().op != opError {
+                                       t.Fatalf("expected op %v, got %v", 
opError, reqErr.Header().op)
+                               }
+                       },
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       f := newErrorFrameForTest(tt.code, tt.msg)
+
+                       got, err := f.parseErrorFrame()
+                       if err != nil {
+                               t.Fatalf("parseErrorFrame returned error: %v", 
err)
+                       }
+
+                       tt.assertT(t, got)
+               })
+       }
+}
+
+func TestParseErrorFrameAllGenericCodes(t *testing.T) {
+       codes := []struct {
+               name string
+               code int
+       }{
+               {"overloaded", ErrCodeOverloaded},
+               {"bootstrapping", ErrCodeBootstrapping},
+               {"invalid", ErrCodeInvalid},
+               {"config", ErrCodeConfig},
+               {"credentials", ErrCodeCredentials},
+               {"syntax", ErrCodeSyntax},
+               {"truncate", ErrCodeTruncate},
+               {"unauthorized", ErrCodeUnauthorized},
+       }
+
+       for _, tc := range codes {
+               t.Run(tc.name, func(t *testing.T) {
+                       f := newErrorFrameForTest(tc.code, "test message")
+
+                       got, err := f.parseErrorFrame()
+                       if err != nil {
+                               t.Fatalf("parseErrorFrame returned error: %v", 
err)
+                       }
+
+                       reqErr, ok := got.(RequestError)
+                       if !ok {
+                               t.Fatalf("expected RequestError, got %T", got)
+                       }
+                       if reqErr.Code() != tc.code {
+                               t.Fatalf("expected code %x, got %x", tc.code, 
reqErr.Code())
+                       }
+               })
+       }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to