Skip to content

encoding/json: Marshal and Unmarshal silently accept floating-point map keys in Go 1.27 #81373

Description

@nnnkkk7

Go version

go version go1.27.0 darwin/arm64 (also reproduces with gotip at 714d9af)

Output of go env in your module/workspace:

AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='clang++'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN='/Users/redacted/go/bin'
GOCACHE='/Users/redacted/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/redacted/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/Users/redacted/src/example/go.mod'
GOMODCACHE='/Users/redacted/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPACKAGESDRIVER=''
GOPATH='/Users/redacted/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/redacted/sdk/go1.27.0'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/redacted/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOVCS=''
GOWORK=''

What did you do?

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	b, err := json.Marshal(map[float64]int{1.5: 1})
	fmt.Printf("Marshal:   out=%q err=%v\n", b, err)

	var m map[float64]int
	err = json.Unmarshal([]byte(`{"1.5":1}`), &m)
	fmt.Printf("Unmarshal: got=%v err=%v\n", m, err)
}

What did you see happen?

With go1.27.0 and gotip (jsonv2 is the default):

Marshal:   out="{\"1.5\":1}" err=<nil>
Unmarshal: got=map[1.5:1] err=<nil>

With go1.24.0, go1.25.5, and go1.27.0 GOEXPERIMENT=nojsonv2:

Marshal:   out="" err=json: unsupported type: map[float64]int
Unmarshal: got=map[] err=json: cannot unmarshal object into Go value of type map[float64]int

Same for map[float32]V and named floating-point key types. NaN and infinite keys still error, though with *json.UnsupportedValueError rather than *json.UnsupportedTypeError.

Floating-point kinds seem to be the only key kinds that succeed silently on a non-empty map. Other unsupported key kinds still error:

map[struct{}]int non-empty     out="" err=json: unsupported value: jsontext: object member name must be a string after offset 2
map[[2]int]int non-empty       out="" err=json: unsupported value: jsontext: object member name must be a string after offset 2
map[complex128]int non-empty   out="" err=json: unsupported type: complex128
map[bool]int non-empty         out="" err=json: unsupported value: jsontext: object member name must be a string after offset 2
map[float64]int non-empty      out="{\"1.5\":1}" err=<nil>
map[float32]int non-empty      out="{\"1.5\":1}" err=<nil>

What did you expect to see?

The same behavior as Go 1.26. The doc comment still says:

Map values encode as JSON objects. The map's key type must either be a string, an integer type, or implement [encoding.TextMarshaler].

and the Go 1.27 release notes say "Marshaling and unmarshaling behavior is preserved, but the exact text of error messages may differ." This is a behavior change, not an error-text change: output produced by 1.27 here can't be read back by Go ≤ 1.26.

#79938 is related, but it was closed on the reasoning that the check is merely deferred and the error is eventually surfaced once a map entry is inserted. That holds for struct/array/bool keys, but not for float keys: the map above is non-empty and never errors, in either direction. Looks like the same class of regression as #81061 and #81355. The cause seems to be that nothing in the v1 compatibility layer restricts map key kinds, and the float arshaler quotes itself in object-name position.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions