-
Notifications
You must be signed in to change notification settings - Fork 593
fix golang oneof with a typed-nil value causes panic. #639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The existing generated code for a oneof field causes a nil-panic if the field contains a typed-nil value. Go correctly infers the value is one of the switch cases but the validation code assumes the value is not a typed-nil. THERE IS AN ARGUMENT THAT THIS BEHAVIOUR IS CORRECT. The authors of go protobuf have stated that any typed-nil in a oneof field is invalid, and so this maybe should cause a panic. Nil panic however is not a helpful error message, and maybe the behaviour should be given as a option to the invoker.
Some points from the wonderful @jhump Bullet two (the change in the PR) seems like the most appropriate course of action. This should be a validation error, not a panic. Bullets 1 and 3 just seem overly harsh -- despite the assertion that a typed nil is not valid (similarly, a nil message inside a repeated field and a nil value in a map with message values are also invalid), these cases are still handled by the core runtime library without panic*. Bullet 4 is overly complicated: I don't think the choice to not panic would be controversial, so a new knob to control the behavior is overkill. An example of all three: syntax = "proto3";
package foo.bar;
message Foo {
map<string, Foo> bar = 1;
repeated Foo baz = 2;
oneof val {
string s = 3;
uint64 i = 4;
}
} Generate Go code and then use the following, which tries to marshal to JSON a message with all three problems: a nil element in a repeated field, a nil value in a map, and a typed-nil oneof. f := Foo{Bar: map[string]*Foo{"foo": &Foo{}, "bar": nil}, Baz: []*Foo{nil}, Val: (*Foo_S)(nil)}
d, err := protojson.Marshal(&f)
if err != nil {
fmt.Print(err)
} else {
fmt.Print(string(d))
} It doesn't even return an error (much less panic)! I just succeeds as if the nil messages were empty and as if the typed-nil were a nil interface (e.g. no oneof set): {"bar":{"bar":{}, "foo":{}}, "baz":[{}]} |
FYI, there is relevant discussion on typed nils in golang/protobuf#1415. From this I gather that treatment of typed-nils is at best inconsistent. For example, from your example proto spec, the generated getter panics (thats actually why I raised that issue in the first place). f := Foo{Val: (*Foo_S)(nil)}
f.GetS() // panics
f.GetI() // correctly returns zero In any case, from your response I can see a couple more options... I don't see the value in 6 as the linked discussion has justification why protojson chose one way over the other, so I'm inclined to go with option 5 (or 2 if we still want to treat oneof as invalid). |
My 2 cents: option 2 still makes the most sense. While some runtime functions may accept questionable input without complaining, this is a validation framework after all, so it seems totally fair to report ostensibly incorrect messages as errors. To that end, it may be worth opening a separate issue to track similar treatment for repeated fields/slices that contain nil messages and map fields with values that are nil messages. |
The existing generated code for a oneof field causes a nil-panic if the field contains a typed-nil value. Go correctly infers the value is one of the switch cases but the validation code assumes the value is not a typed-nil.
NOTE: THERE IS AN ARGUMENT THAT THIS BEHAVIOUR IS CORRECT.
The authors of go protobuf have stated that any typed-nil in a oneof field is invalid, and so this maybe should cause a panic. Nil panic however is not a helpful error message. I see a few ways forward.