- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 178
 
Description
Summary
goccy/go-json panics when marshaling a recursive struct that contains a map[string]any field along with a slice of the same struct type (recursive children).
Environment
- Go version: 
go version go1.25.3 darwin/arm64 - goccy/go-json version: 
v0.10.5 - OS: macOS (Darwin 25.0.0)
 
Expected Behavior
The library should successfully marshal the struct, just like the standard library encoding/json.
Actual Behavior
The library panics with:
panic: runtime error: invalid memory address or nil pointer dereference(in some cases)panic: runtime error: slice bounds out of range [206158430215:145](in other cases)
Minimal Reproduction Code
package main
import (
	"encoding/json"
	"fmt"
	goccyjson "github.com/goccy/go-json"
)
type Node struct {
	Label    string         `json:"label"`
	Value    string         `json:"value"`
	Meta     map[string]any `json:"meta,omitempty"`
	Children []Node         `json:"children,omitempty"`
}
func main() {
	// This panics with goccy/go-json but works fine with encoding/json
	tree := []Node{
		{
			Label: "Parent",
			Value: "p1",
			Meta: map[string]any{
				"code": "parent",
				"desc": "Parent node",
			},
			Children: []Node{
				{
					Label: "Child1",
					Value: "c1",
					Meta: map[string]any{
						"code": "child1",
						"desc": "First child",
					},
				},
			},
		},
	}
	// Standard library - works fine
	stdResult, stdErr := json.Marshal(tree)
	if stdErr != nil {
		fmt.Printf("stdlib error: %v\n", stdErr)
	} else {
		fmt.Printf("stdlib success: %d bytes\n", len(stdResult))
	}
	// goccy/go-json - PANICS
	goccyResult, goccyErr := goccyjson.Marshal(tree)
	if goccyErr != nil {
		fmt.Printf("goccy error: %v\n", goccyErr)
	} else {
		fmt.Printf("goccy success: %d bytes\n", len(goccyResult))
	}
}Steps to Reproduce
- Save the code above to 
main.go - Run 
go mod init test && go get github.com/goccy/[email protected] - Run 
go run main.go - Observe the panic
 
Stack Trace
Panic 1 (nil pointer dereference):
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x5 pc=0x102405b14]
goroutine 1 [running]:
github.com/goccy/go-json/internal/encoder/vm.ptrToString(...)
	.../internal/encoder/vm/util.go:89
github.com/goccy/go-json/internal/encoder/vm.Run(0x140001804e0, {0x140001bc400?, 0x0?, 0x400?}, 0x14000194070?)
	.../internal/encoder/vm/vm.go:1718 +0x4754
github.com/goccy/go-json.encodeRunCode(...)
	.../encode.go:310 +0x64
github.com/goccy/go-json.encode(...)
	.../encode.go:235 +0x1e4
github.com/goccy/go-json.marshal(...)
	.../encode.go:150 +0xb8
github.com/goccy/go-json.Marshal(...)
	.../json.go:170 +0x30
Panic 2 (slice bounds out of range):
panic: runtime error: slice bounds out of range [206158430215:145]
goroutine 1 [running]:
github.com/goccy/go-json/internal/encoder/vm.Run(0x140001a6680, {0x140001d8000?, 0x140001a6410?, 0x18?}, 0x18?)
	.../internal/encoder/vm/vm.go:440 +0x21d44
github.com/goccy/go-json.encodeRunCode(...)
	.../encode.go:310 +0x64
github.com/goccy/go-json.encode(...)
	.../encode.go:235 +0x1e4
github.com/goccy/go-json.marshal(...)
	.../encode.go:150 +0xb8
github.com/goccy/go-json.Marshal(...)
	.../json.go:170 +0x30
Analysis
The bug appears when all of the following conditions are met:
- Struct has a 
map[string]anyfield - Struct has a recursive field (e.g., 
[]Node) - Both the parent and children nodes have non-empty 
Metamaps 
Removing any of these conditions makes the panic disappear:
- ✅ Works: Struct without 
Metafield - ✅ Works: Struct with 
Metabut withoutChildren - ❌ Panics: Struct with both 
MetaandChildrencontainingMeta 
Workaround
For now, switching back to the standard library encoding/json resolves the issue:
import "encoding/json"
result, err := json.Marshal(tree)  // Use stdlib insteadAdditional Context
This bug was discovered while implementing a tree options API that returns hierarchical data structures with metadata. The same code works perfectly with encoding/json but consistently panics with goccy/go-json.
The issue seems to be in the encoder VM's handling of recursive structures combined with map[string]any fields, possibly related to pointer management or slice indexing calculations in the encoder code generation.