Skip to content

Format runtime error: index out of range [0] with length 0 #855

@SirNexus

Description

@SirNexus

I am experiencing a panic in the gomega code. Here is the stack trace:

  [PANICKED] Test Panicked
  In [It] at: /Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/internal/async_assertion.go:333 @ 07/24/25 08:08:38.852

  runtime error: index out of range [0] with length 0

  Full Stack Trace
    github.com/onsi/gomega/internal.(*AsyncAssertion).buildActualPoller.func3.1()
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/internal/async_assertion.go:333 +0x194
    panic({0x103e79fc0?, 0x140007373e0?})
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/go/src/runtime/panic.go:792 +0x124
    github.com/onsi/gomega/format.formatMap({0x103b307c0?, 0x14000997920?, 0x103bd75e0?}, 0x1)
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/format/format.go:446 +0x364
    github.com/onsi/gomega/format.formatValue({0x103b307c0, 0x14000997920, 0x15}, 0x1)
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/format/format.go:378 +0x5e0
    github.com/onsi/gomega/format.Object({0x103b307c0, 0x14000997920}, 0x1)
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/format/format.go:265 +0x1a4
    github.com/onsi/gomega/matchers.(*HaveLenMatcher).FailureMessage(0x140005ac720, {0x103b307c0?, 0x14000997920?})
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/matchers/have_len_matcher.go:23 +0x34
    github.com/onsi/gomega/internal.(*Assertion).match(0x140009f24c0, {0x104230368, 0x140005ac720}, 0x1, {0x0, 0x0, 0x0})
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/internal/assertion.go:101 +0xd8
    github.com/onsi/gomega/internal.(*Assertion).To(0x140009f24c0, {0x104230368, 0x140005ac720}, {0x0, 0x0, 0x0})
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/internal/assertion.go:62 +0xa8
    reflect.Value.call({0x103852900?, 0x14000881530?, 0x0?}, {0x10313bfc2, 0x4}, {0x140008815a8, 0x1, 0x103ff85c0?})
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/go/src/reflect/value.go:584 +0x978
    reflect.Value.Call({0x103852900?, 0x14000881530?, 0x103ff85c0?}, {0x140008815a8?, 0x103852900?, 0x14000881530?})
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/go/src/reflect/value.go:368 +0x94
    github.com/onsi/gomega/internal.(*AsyncAssertion).buildActualPoller.func3()
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/internal/async_assertion.go:337 +0xd4
    github.com/onsi/gomega/internal.(*AsyncAssertion).match(0x14000287730, {0x1042303c8, 0x106b20240}, 0x1, {0x0, 0x0, 0x0})
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/internal/async_assertion.go:410 +0x104
    github.com/onsi/gomega/internal.(*AsyncAssertion).Should(0x14000287730, {0x1042303c8, 0x106b20240}, {0x0, 0x0, 0x0})
    	/Users/aidancarson/.asdf/installs/golang/1.24.2/packages/pkg/mod/github.com/onsi/gomega@v1.37.0/internal/async_assertion.go:145 +0x7c

In my code, I am checking if a map has a length of 1:

g.Expect(<map>).To(HaveLen(1))

I've investigated it a bit, and it looks like when my code uses a HaveLen(1) matcher, on a failure the HaveLen matcher calls format:

func (matcher *HaveLenMatcher) FailureMessage(actual any) (message string) {
	return fmt.Sprintf("Expected\n%s\nto have length %d", format.Object(actual, 1), matcher.Count) // Calls format
}

And when formatting the object within formatMap (format.go:439), on this line (format.go:446):

func formatMap(v reflect.Value, indentation uint) string {
	l := v.Len()
	result := make([]string, l)

	longest := 0
	for i, key := range v.MapKeys() {
		value := v.MapIndex(key)
		result[i] = fmt.Sprintf("%s: %s", formatValue(key, indentation+1), formatValue(value, indentation+1)) // This panics
		if len(result[i]) > longest {
			longest = len(result[i])
		}
	}

There is another goroutine which is modifying the map and adding a value asynchronously, and so I'm thinking what the problem is: We are panicing because the underlying value v changes. So we create a result array with the size of v.Len(), but then v.MapKeys() returns a different (greater) number of values than we've allocated an array for -- causing a panic

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions