go: panic: reflect: call of reflect.Value.IsZero on zero Value

What version of Go are you using (go version)?

$ go version
go version go1.16.4 linux/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

Operating System is XUbuntu 20.10 (amd64). CPU is Intel Xeon E5-2678 v3

GOARCH=“amd64” GOHOSTARCH=“amd64” GOHOSTOS=“linux” GOOS=“linux”

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/***/.cache/go-build"
GOENV="/home/***/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/***/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/***/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.16.4"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build740936505=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I try to run the IsZero() Method on an Object of reflect.Value Type. In the Debugger I reach the Code which in Comments is said to be unreachable.

panic: reflect: call of reflect.Value.IsZero on zero Value

goroutine 1 [running]:
reflect.Value.IsZero(0x0, 0x0, 0x0, 0x0)
        /usr/local/go/src/reflect/value.go:1134 +0x70b
// IsZero reports whether v is the zero value for its type.
// It panics if the argument is invalid.
func (v Value) IsZero() bool {
	switch v.kind() {

//...

default:
		// This should never happens, but will act as a safeguard for
		// later, as a default value doesn't makes sense here.
		panic(&ValueError{"reflect.Value.IsZero", v.Kind()})
	}
}

https://play.golang.org/p/o5j2PKXxpwJ

package main

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

type JsonObject = map[string]interface{}

type ResponseRaw struct {
	Records []JsonObject
}

func main() {
	text := `[{"x": 1, "y": 2}, {"z": 3, "zz": 4, "step": 123}, {}, {"id": 999}, {"q": "test", "lto": {"a": 8, "step": 456}}, null]`
	rr, err := parseValidJson([]byte(text))
	if err != nil {
		log.Println(err)
	}
	fmt.Println("rr:", rr)
}

func parseValidJson(jsonText []byte) (rr *ResponseRaw, err error) {
	responseAnonymous := make([]interface{}, 0)

	err = json.Unmarshal(jsonText, &responseAnonymous)
	if err != nil {
		return nil, err
	}

	rr = &ResponseRaw{
		Records: make([]JsonObject, 0),
	}

	for _, arrayItem := range responseAnonymous {
		arrayItemAsJsonObject, ok := arrayItem.(JsonObject)
		if !ok {
			arrayItemValue := reflect.ValueOf(arrayItem)
			// Here we may receive an empty Value, i.e. reflect.Value{}.
			// When we receive it and check it for Zero Value, we reach that
			// Part of the Code which should be unreachable.
			if arrayItemValue.IsZero() {
				continue
			}
			if arrayItemValue.IsNil() {
				continue
			}
			// The built-in JSON Parser thinks that an empty Object '{}' is an
			// empty Slice of Objects. It may be a Bug in the Parser.
			arrayItemType := reflect.TypeOf(arrayItem)
			if arrayItemType.Kind() == reflect.Slice {
				continue
			}

			return nil, errors.New("json object parsing error")
		}

		rr.Records = append(rr.Records, arrayItemAsJsonObject)
	}

	return rr, nil
}

What did you expect to see?

I expected to see the reflect.Value.IsZero() Method returning a boolean Value.

What did you see instead?

I saw the reflect.Value.IsZero() Method reaching the unreachable code and panicing.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 1
  • Comments: 16 (4 by maintainers)

Most upvoted comments

Can’t add anything useful, but will say I ended up there today as well!

Basically, one needs to call IsValid before IsZero, thanks for that clarification at least.

Can’t call IsZero() on zero Value.

This panic message is counter-intuitive and confusing.

The Comments in the Library are wrong and misleading.

did a search on google and ended up here. This piece of documentation is misleading. Users would expect X.isZero() to return true when X is zero instead of a panic.

@seankhliao perhaps we could discuss how the documentation for IsZero could be improved to clarify the point you just highlighted? I do agree with @vault-thirteen that it’s not entirely clear, and the panic error message is a unhelpful.

Just wanted to put another +1 that having the documentation of other reflect functions say something like: “an invalid zero value” would have saved me a lot of time trying to figure out why IsZero was panicking on what it was seemingly designed for. Thanks to everyone who worked through this before me so I could move forward with my project!

Add a Printf like so

fmt.Printf("%#v\n", arrayItem)
arrayItemValue := reflect.ValueOf(arrayItem)

You are calling reflect.ValueOf(nil) Since nil has no type, it reaches the default case.