Skip to content

Commit 7294a56

Browse files
committed
Add option to validate responses
Signed-off-by: bufdev <bufdev-github@buf.build>
1 parent cae5c14 commit 7294a56

File tree

10 files changed

+213
-85
lines changed

10 files changed

+213
-85
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
runs-on: ubuntu-latest
1616
strategy:
1717
matrix:
18-
go-version: [1.21.x, 1.22.x, 1.23.x]
18+
go-version: [1.24.x, 1.25.x]
1919
steps:
2020
- name: Checkout Code
2121
uses: actions/checkout@v4
@@ -32,5 +32,5 @@ jobs:
3232
# conflicting guidance, run only on the most recent supported version.
3333
# For the same reason, only check generated code on the most recent
3434
# supported version.
35-
if: matrix.go-version == '1.23.x'
35+
if: matrix.go-version == '1.25.x'
3636
run: make checkgenerate && make lint

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export PATH := $(BIN):$(PATH)
1111
export GOBIN := $(abspath $(BIN))
1212
COPYRIGHT_YEARS := 2023-2025
1313
LICENSE_IGNORE := --ignore testdata/
14+
BUF_VERSION = v1.57.2
1415

1516
.PHONY: help
1617
help: ## Describe useful make targets
@@ -70,11 +71,11 @@ checkgenerate:
7071

7172
$(BIN)/buf: Makefile
7273
@mkdir -p $(@D)
73-
go install github.com/bufbuild/buf/cmd/buf@v1.50.0
74+
go install github.com/bufbuild/buf/cmd/buf@$(BUF_VERSION)
7475

7576
$(BIN)/license-header: Makefile
7677
@mkdir -p $(@D)
77-
go install github.com/bufbuild/buf/private/pkg/licenseheader/cmd/license-header@v1.50.0
78+
go install github.com/bufbuild/buf/private/pkg/licenseheader/cmd/license-header@$(BUF_VERSION)
7879

7980
$(BIN)/golangci-lint: Makefile
8081
@mkdir -p $(@D)

RELEASE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Releasing
2+
3+
This document outlines how to create a release of validate-go.
4+
5+
1. Clone the repo, ensuring you have the latest main.
6+
7+
2. Review all commits in the new release and for each PR check an appropriate label is used and edit
8+
the title to be meaningful to end users.
9+
10+
3. Using the Github UI, create a new release.
11+
12+
- Under “Choose a tag”, type in “vX.Y.Z” to create a new tag for the release upon publish.
13+
- Target the main branch.
14+
- Title the Release “vX.Y.Z”.
15+
- Click “set as latest release”.
16+
- Set the last version as the “Previous tag”.
17+
- Edit the release notes.
18+
19+
4. Publish the release.

go.mod

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
module connectrpc.com/validate
22

3-
go 1.23.0
3+
go 1.24.0
44

55
require (
6-
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1
7-
buf.build/go/protovalidate v0.11.0
8-
connectrpc.com/connect v1.18.1
9-
github.com/stretchr/testify v1.10.0
10-
google.golang.org/protobuf v1.36.6
6+
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.9-20250912141014-52f32327d4b0.1
7+
buf.build/go/protovalidate v1.0.0
8+
connectrpc.com/connect v1.19.0
9+
github.com/stretchr/testify v1.11.1
10+
google.golang.org/protobuf v1.36.9
1111
)
1212

1313
require (
14-
cel.dev/expr v0.23.1 // indirect
15-
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
14+
cel.dev/expr v0.24.0 // indirect
15+
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
1616
github.com/davecgh/go-spew v1.1.1 // indirect
17-
github.com/google/cel-go v0.25.0 // indirect
17+
github.com/google/cel-go v0.26.1 // indirect
1818
github.com/pmezard/go-difflib v1.0.0 // indirect
19-
github.com/stoewer/go-strcase v1.3.0 // indirect
20-
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
21-
golang.org/x/text v0.23.0 // indirect
22-
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect
23-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect
19+
github.com/stoewer/go-strcase v1.3.1 // indirect
20+
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
21+
golang.org/x/text v0.29.0 // indirect
22+
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9 // indirect
23+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 // indirect
2424
gopkg.in/yaml.v3 v3.0.1 // indirect
2525
)

go.sum

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1-
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1 h1:YhMSc48s25kr7kv31Z8vf7sPUIq5YJva9z1mn/hAt0M=
2-
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U=
3-
buf.build/go/protovalidate v0.11.0 h1:qmX+1Z/t5lqlxQW6bNHnCGE9kX6yXBNul0jYFjD2r3Q=
4-
buf.build/go/protovalidate v0.11.0/go.mod h1:Ryhm9EyqOxO/jdqEBpH4mI/FUk2ULyYeuhR+QRhOgqc=
5-
cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg=
6-
cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
7-
connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw=
8-
connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
9-
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
10-
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
1+
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.9-20250912141014-52f32327d4b0.1 h1:DQLS/rRxLHuugVzjJU5AvOwD57pdFl9he/0O7e5P294=
2+
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.9-20250912141014-52f32327d4b0.1/go.mod h1:aY3zbkNan5F+cGm9lITDP6oxJIwu0dn9KjJuJjWaHkg=
3+
buf.build/go/protovalidate v1.0.0 h1:IAG1etULddAy93fiBsFVhpj7es5zL53AfB/79CVGtyY=
4+
buf.build/go/protovalidate v1.0.0/go.mod h1:KQmEUrcQuC99hAw+juzOEAmILScQiKBP1Oc36vvCLW8=
5+
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
6+
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
7+
connectrpc.com/connect v1.19.0 h1:LuqUbq01PqbtL0o7vn0WMRXzR2nNsiINe5zfcJ24pJM=
8+
connectrpc.com/connect v1.19.0/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
9+
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
10+
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
1111
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1212
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1313
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
14-
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
15-
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
16-
github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
17-
github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=
14+
github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ=
15+
github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
1816
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
1917
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
2018
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@@ -23,28 +21,26 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
2321
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
2422
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2523
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
26-
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
27-
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
24+
github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
25+
github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
2826
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
2927
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
3028
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
3129
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
3230
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
3331
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
34-
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
35-
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
36-
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
37-
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
38-
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
39-
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
40-
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
41-
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
42-
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw=
43-
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
44-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=
45-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
46-
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
47-
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
32+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
33+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
34+
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
35+
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
36+
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
37+
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
38+
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9 h1:jm6v6kMRpTYKxBRrDkYAitNJegUeO1Mf3Kt80obv0gg=
39+
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9/go.mod h1:LmwNphe5Afor5V3R5BppOULHOnt2mCIf+NxMd4XiygE=
40+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 h1:V1jCN2HBa8sySkR5vLcCSqJSTMv093Rw9EJefhQGP7M=
41+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
42+
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
43+
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
4844
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
4945
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
5046
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

internal/gen/example/calculator/v1/calculator.pb.go

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/gen/example/user/v1/user.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/proto/example/calculator/v1/calculator.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ message CumSumRequest {
2323
}
2424

2525
message CumSumResponse {
26-
int64 sum = 1;
26+
int64 sum = 1 [(buf.validate.field).int64.gt = 0];
2727
}
2828

2929
service CalculatorService {

validate.go

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,26 @@ func WithValidator(validator protovalidate.Validator) Option {
4343
})
4444
}
4545

46+
// WithValidateResponses configures the [Interceptor] to also validate reponses
47+
// in addition to validating requests.
48+
//
49+
// By default:
50+
//
51+
// - Unary: Response messages from the server are not validated.
52+
// - Client streams: Received messages are not validated.
53+
// - Server streams: Sent messages are not validated.
54+
//
55+
// However, these messages are all validated if this option is set.
56+
func WithValidateResponses() Option {
57+
return optionFunc(func(i *Interceptor) {
58+
i.validateResponses = true
59+
})
60+
}
61+
4662
// Interceptor is a [connect.Interceptor] that ensures that RPC request
4763
// messages match the constraints expressed in their Protobuf schemas. It does
48-
// not validate response messages.
64+
// not validate response messages unless the [WithValidateResponses] option
65+
// is specified.
4966
//
5067
// By default, Interceptors use a validator that lazily compiles constraints
5168
// and works with any Protobuf message. This is a simple, widely-applicable
@@ -63,7 +80,8 @@ func WithValidator(validator protovalidate.Validator) Option {
6380
//
6481
// [detailed representation of the error]: https://pkg.go.dev/buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate#Violations
6582
type Interceptor struct {
66-
validator protovalidate.Validator
83+
validator protovalidate.Validator
84+
validateResponses bool
6785
}
6886

6987
// NewInterceptor builds an Interceptor. The default configuration is
@@ -91,7 +109,16 @@ func (i *Interceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
91109
if err := validate(i.validator, req.Any()); err != nil {
92110
return nil, err
93111
}
94-
return next(ctx, req)
112+
response, err := next(ctx, req)
113+
if err != nil {
114+
return response, err
115+
}
116+
if i.validateResponses {
117+
if err := validate(i.validator, response.Any()); err != nil {
118+
return response, err
119+
}
120+
}
121+
return response, nil
95122
}
96123
}
97124

@@ -100,7 +127,7 @@ func (i *Interceptor) WrapStreamingClient(next connect.StreamingClientFunc) conn
100127
return func(ctx context.Context, spec connect.Spec) connect.StreamingClientConn {
101128
return &streamingClientInterceptor{
102129
StreamingClientConn: next(ctx, spec),
103-
validator: i.validator,
130+
interceptor: i,
104131
}
105132
}
106133
}
@@ -110,42 +137,64 @@ func (i *Interceptor) WrapStreamingHandler(next connect.StreamingHandlerFunc) co
110137
return func(ctx context.Context, conn connect.StreamingHandlerConn) error {
111138
return next(ctx, &streamingHandlerInterceptor{
112139
StreamingHandlerConn: conn,
113-
validator: i.validator,
140+
interceptor: i,
114141
})
115142
}
116143
}
117144

118145
type streamingClientInterceptor struct {
119146
connect.StreamingClientConn
120147

121-
validator protovalidate.Validator
148+
interceptor *Interceptor
122149
}
123150

124151
func (s *streamingClientInterceptor) Send(msg any) error {
125-
if err := validate(s.validator, msg); err != nil {
152+
if err := validate(s.interceptor.validator, msg); err != nil {
126153
return err
127154
}
128155
return s.StreamingClientConn.Send(msg)
129156
}
130157

158+
func (s *streamingClientInterceptor) Receive(msg any) error {
159+
if err := s.StreamingClientConn.Receive(msg); err != nil {
160+
return err
161+
}
162+
if s.interceptor.validateResponses {
163+
return validate(s.interceptor.validator, msg)
164+
}
165+
return nil
166+
}
167+
131168
type streamingHandlerInterceptor struct {
132169
connect.StreamingHandlerConn
133170

134-
validator protovalidate.Validator
171+
interceptor *Interceptor
172+
}
173+
174+
func (s *streamingHandlerInterceptor) Send(msg any) error {
175+
if s.interceptor.validateResponses {
176+
if err := validate(s.interceptor.validator, msg); err != nil {
177+
return err
178+
}
179+
}
180+
return s.StreamingHandlerConn.Send(msg)
135181
}
136182

137183
func (s *streamingHandlerInterceptor) Receive(msg any) error {
138184
if err := s.StreamingHandlerConn.Receive(msg); err != nil {
139185
return err
140186
}
141-
return validate(s.validator, msg)
187+
return validate(s.interceptor.validator, msg)
142188
}
143189

144190
type optionFunc func(*Interceptor)
145191

146192
func (f optionFunc) apply(i *Interceptor) { f(i) }
147193

148194
func validate(validator protovalidate.Validator, msg any) error {
195+
if msg == nil {
196+
return nil
197+
}
149198
protoMsg, ok := msg.(proto.Message)
150199
if !ok {
151200
return fmt.Errorf("expected proto.Message, got %T", msg)

0 commit comments

Comments
 (0)