Go 1.27 adds a new JSON "inline" struct tag as part of #71497. This tag specifies that the JSON object representation of this field is to be promoted as if it were specified in the parent struct. It is the JSON equivalent of Go struct embedding. A Go embedded field is implicitly inlined unless an explicit JSON name is specified.
In Go 1.27, this tag is supported by both the encoding/json and encoding/json/v2 APIs. Support in encoding/json is intentional to ensure that packages using new tags on their types don't implicitly require all of their dependents switch to the encoding/json/v2 API.
Even though this tag doesn't exist in 1.26, it turns out that it has widespread use throughout public Go code, particularly within the Kubernetes community. https://grep.app/search?q=%60json%3A%22%2Cinline%22%60 shows ~29k results for uses on the "inline" tag.
I do not know the original source of this tag in Kubernetes. It seems to have originated at some point and then got copied around. In fact, @liggitt has been removing uses of this tag since it didn't actually do anything (kubernetes/kubernetes#138260).
Regardless, Kubernetes uses encoding/json and many of these tags still exist, so there is a risk of behavior change when the 1.27 toolchain upgrade occurs and encoding/json gains support for this tag.
29k uses sounds dire, but the vast majority of uses apply the tag to an embedded field. Embedded fields are already inlined, so the "inline" tag has no impact to these fields.
I analyzed modules in the Go module proxy with at 3 transitive importers for uses of the "inline" tag on named fields. The full set of 2639 findings are here.
While I have not looked at every finding, some things I've found so far:
All told, I have not yet found any cases that are definitively broken by a 1.27 behavior change, but I do not have high confidence that there are none.
I'm creating this issue to discuss what we should do about this. Currently I see four options:
- Simply do nothing because we don't believe that this will cause ecosystem disruption.
- Rename the "inline" tag to something with no existing use in the ecosystem to avoid potential conflicts.
- Drop the "inline" tag entirely, and reconsider it for 1.28.
- Add a compatibility GODEBUG that gates the "inline" tag behind the language version. See the full description of this below.
cc @dsnet @aclements @ChrisHines @neild @liggitt
Go 1.27 adds a new JSON "inline" struct tag as part of #71497. This tag specifies that the JSON object representation of this field is to be promoted as if it were specified in the parent struct. It is the JSON equivalent of Go struct embedding. A Go embedded field is implicitly inlined unless an explicit JSON name is specified.
In Go 1.27, this tag is supported by both the
encoding/jsonandencoding/json/v2APIs. Support inencoding/jsonis intentional to ensure that packages using new tags on their types don't implicitly require all of their dependents switch to theencoding/json/v2API.Even though this tag doesn't exist in 1.26, it turns out that it has widespread use throughout public Go code, particularly within the Kubernetes community. https://grep.app/search?q=%60json%3A%22%2Cinline%22%60 shows ~29k results for uses on the "inline" tag.
I do not know the original source of this tag in Kubernetes. It seems to have originated at some point and then got copied around. In fact, @liggitt has been removing uses of this tag since it didn't actually do anything (kubernetes/kubernetes#138260).
Regardless, Kubernetes uses
encoding/jsonand many of these tags still exist, so there is a risk of behavior change when the 1.27 toolchain upgrade occurs andencoding/jsongains support for this tag.29k uses sounds dire, but the vast majority of uses apply the tag to an embedded field. Embedded fields are already inlined, so the "inline" tag has no impact to these fields.
I analyzed modules in the Go module proxy with at 3 transitive importers for uses of the "inline" tag on named fields. The full set of 2639 findings are here.
While I have not looked at every finding, some things I've found so far:
github.com/openai/openai-goor copies of this code. This module uses a custom json package (github.com/openai/openai-go/internal/apijson) that supports an "inline" tag with semantics similar to Go 1.27. It does not appear that these types are ever used withencoding/json. Rudimentary filtering of the findings to exclude these packages reduces the list to 694 findings. The full filtered list is here.LoadFingersfunction that unmarshals this type withencoding/json, which 1.27 will do differently. Ironically I can't find any tests in this module that actually care about the inlined fields, so I don't have confirmation that it is broken.encoding/json.All told, I have not yet found any cases that are definitively broken by a 1.27 behavior change, but I do not have high confidence that there are none.
I'm creating this issue to discuss what we should do about this. Currently I see four options:
cc @dsnet @aclements @ChrisHines @neild @liggitt