Skip to content
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

proposal: encoding/json: include field name in unmarshal error messages for types that implement UnmarshalJSON #71958

Closed
MadLittleMods opened this issue Feb 26, 2025 · 3 comments
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool Proposal
Milestone

Comments

@MadLittleMods
Copy link

MadLittleMods commented Feb 26, 2025

Proposal Details

Problem context

The following example just outputs panic: unhelpful error which isn't useful in finding out which key in the JSON caused the problem, i.e "was it bar or baz that caused the problem?" We only have access to the value bytes (no key name) so even if we wanted to write a better error message, we couldn't.

package main

import (
	"encoding/json"
	"errors"
	"fmt"
)

type strawberry struct{}

func (b *strawberry) UnmarshalJSON(data []byte) error {
	return errors.New("unhelpful error")
}

type myType struct {
	Foo string     `json:"foo,omitempty"`
	Bar strawberry `json:"bar,omitempty"`
	Baz strawberry `json:"baz,omitempty"`
}

func main() {
	json_bytes := []byte(`{ "foo": "test", "bar": 1, "baz": 2 }`)
	var data myType
	if err := json.Unmarshal(json_bytes, &data); err != nil {
		panic(err)
	}
	fmt.Println(data)
}

Compare this with the built-in types where we get some helpful output -> panic: json: cannot unmarshal number into Go struct field myType.bar of type string because of the automatic logic behind json.UnmarshalTypeError (see addErrorContext).

  • Playground link
  • Output: panic: json: cannot unmarshal number into Go struct field myType.bar of type string
package main

import (
	"encoding/json"
	"fmt"
)

type myType struct {
	Foo string `json:"foo,omitempty"`
	Bar string `json:"bar,omitempty"`
	Baz string `json:"baz,omitempty"`
}

func main() {
	json_bytes := []byte(`{ "foo": "test", "bar": 1, "baz": 2 }`)
	var data myType
	if err := json.Unmarshal(json_bytes, &data); err != nil {
		panic(err)
	}
	fmt.Println(data)
}

We can partially get our desired effect by returning json.UnmarshalTypeError ourselves but it doesn't allow for any extra context on why we failed to parse it. For example, what if the reason we failed to unmarshal was because Strawberries have at-least 200 seeds but there is not place to add this context with the current json.UnmarshalTypeError.

  • Playground link
  • Output: panic: json: cannot unmarshal 1 into Go struct field myType.bar of type main.strawberry
package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)

type strawberry struct{}

func (b *strawberry) UnmarshalJSON(data []byte) error {
	return &json.UnmarshalTypeError{Value: string(data), Type: reflect.TypeOf((*strawberry)(nil)).Elem(), Offset: 0}
}

type myType struct {
	Foo string     `json:"foo,omitempty"`
	Bar strawberry `json:"bar,omitempty"`
	Baz strawberry `json:"baz,omitempty"`
}

func main() {
	json_bytes := []byte(`{ "foo": "test", "bar": 1, "baz": 2 }`)
	var data myType
	if err := json.Unmarshal(json_bytes, &data); err != nil {
		panic(err)
	}
	fmt.Println(data)
}

Proposal

Wrap any error from UnmarshalJSON and include the field name context that generated the error.

Another possibility is to extend json.UnmarshalTypeError to allow for additional custom error context which would work but not everyone is going to be as meticulous to always return json.UnmarshalTypeError to get the benefits.

I'm not writing Go everyday so there is probably a better strategy as well.

Related issues

@gopherbot gopherbot added this to the Proposal milestone Feb 26, 2025
@ianlancetaylor
Copy link
Member

Thanks. At present any proposals for encoding/json should be directed at encoding/json/v2 instead. See #71497 .

@MadLittleMods
Copy link
Author

@gabyhelp gabyhelp added the LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool label Feb 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool Proposal
Projects
None yet
Development

No branches or pull requests

4 participants