square / square/wire

Singular fields in Proto 3 are incorrectly treated as required

Open
#2,407 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Kotlin
Stars
4.4k
Forks
627
Avg merge
3d 15m
Merged PRs (30d)
20

Description

Wire seems to treat singular fields in Proto 3 schemas as if they are required, generating non-nullable setters / getters / etc.

This seems to conflict with the behaviour described in the protobuf language guide, which describes singular as being closer to optional than to required.

Specifically, I would expect that a singular field in a proto3 definition would generate a "setter" function that accepts null as input (clearing the value), and a non-nullable "getter" function that substitutes the appropriate default if no value is present in the serialised message.

Example:

syntax = "proto2";
message Proto2Format {
  optional string field_1 = 1;
  optional string field_2 = 2;
}
syntax = "proto3";
message Proto3Format {
  string field_1 = 1;
  optional string field_2 = 2;
}
fun proto2ToProto3() {
  val proto2Message = Proto2Format.Builder().field_1(null).field_2(null).build()
  val proto3Message = Proto3Format.ADAPTER.decode(Proto2Format.ADAPTER.encode(proto2Message))
  assertThat(proto3Message.field_1).isEqualTo("") // Default is used because value was not serialised
  assertThat(proto3Message.field_2).isNull
}

fun proto3ToProto2() {
  val proto3Message = Proto3Format.Builder().field_1("").field_2(null).build()
  val proto2Message = Proto2Format.ADAPTER.decode(Proto3Format.ADAPTER.encode(proto3Message))
  assertThat(proto2Message.field_1).isNull // Value was not serialised because it was the default
  assertThat(proto2Message.field_2).isNull
}

fun proto3() {
  val proto3Message = Proto3Format.Builder().field_1(null).field_2(null).build()
  assertThat(proto3Message.field_1).isEqualTo("") // Default is used
  assertThat(proto3Message.field_2).isNull
}

Currently, if we fail to supply a value for singular fields, we encounter exceptions like the following:

java.lang.NullPointerException: Parameter specified as non-null is null: method com.my.namespace.MyWireClass.<init>, parameter my_singular_field

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No source file or test is named in the issue. Start by reproducing the Kotlin examples with the supplied proto2 and proto3 definitions, then trace Wire's proto3 singular-field code generation and encoding behavior. Done means singular fields accept null in builders and use the protobuf default when absent, while optional fields remain nullable.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
api
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.