Skip to content

proto3 dropped the required keyword, and editions did not bring it back except as a legacy feature for proto2 files that already use it. Required fields get in the way of schema evolution. Every message has to include a required field. If you stop sending it, every older reader fails to decode the new message, and there is no way to retire the field gradually. Protobuf's own maintainers came to see required as a mistake, and the designers of Cap'n Proto reached the same conclusion.

You can still require a field. The check happens in validation now, which can change without breaking the wire format. Tightening a rule can still reject requests that existing clients send, so roll it out like any other API change. In application code, generated getters return the zero value for a missing field and your handler checks for it. In the schema, a custom option declares the field required and a validator enforces it when a message is checked rather than when it is decoded, so the field stays wire compatible. protovalidate's required rule is the common choice, and since the mechanism is just options, you can define and check your own the same way.

Metadata validation
import "buf/validate/validate.proto";

message CreateUserRequest {
  // Required at the validation layer
  // but optional at the wire layer.
  string email = 1 [
    (buf.validate.field).string.email = true,
    (buf.validate.field).required = true
  ];
}
Application validation (Go)
// Safe access even if req is nil
if req.GetEmail() == "" {
    return status.Error(codes.InvalidArgument, "email is required")
}

Further Reading

Next

Size Limits

Practical size limits for Protobuf messages: the 2GB hard ceiling, recursion and memory behavior, and how to design schemas for large payloads.