Skip to content

hash of unhashable type main.ID (struct containing slice) #26

@veggiemonk

Description

@veggiemonk

Describe the bug
When using a struct containing a slice as a key in the TypeSchemas map in jsonschema.ForOptions, the program panics with runtime error: hash of unhashable type. This occurs because slices are not hashable in Go and cannot be used as map keys.

To Reproduce
Steps to reproduce the behavior:

  1. Define a struct that contains a slice field.
  2. Create an instance of this struct and use it as a key in a map[any]*jsonschema.Schema.
  3. Assign this map to the TypeSchemas field of jsonschema.ForOptions.
  4. Attempt to print or otherwise inspect the jsonschema.ForOptions struct, which will trigger the panic.

Here is a minimal code example that reproduces the bug:

Go playground link: https://go.dev/play/p/N69KcaSAjzZ

package main

import (
	"bytes"
	"fmt"

	"github.com/google/jsonschema-go/jsonschema"
)

type ID struct {
	seg []int
}

func (t ID) String() string {
	buf := bytes.Buffer{}
	for i, s := range t.seg {
		if i > 0 {
			buf.WriteString(".")
		}
		buf.WriteString(fmt.Sprintf("%02d", s))
	}
	return buf.String()
}

func main() {
	m := map[any]*jsonschema.Schema{
		ID{}: {Type: "string"},
	}

	forOpts := jsonschema.ForOptions{TypeSchemas: m}
	fmt.Printf("%+v\n", forOpts)
}

Expected behavior
The program should not panic. There are a few possible expected behaviors:

  1. The library could detect the unhashable key and provide a clear error message, rather than panicking. (example: switching from any to comparable)
  2. The library could internally handle such cases, perhaps by converting the key to a string representation if it implements fmt.Stringer, before using it in a map. I don't like this as it feels brittle.
  3. Create an interface with a method Hash() int64 but there are a few proposals about hashing:

Logs

panic: runtime error: hash of unhashable type main.ID

goroutine 1 [running]:
main.main()
        /Users/julien/perso/bug01/main.go:26 +0x6c
exit status 2

Additional context
I wanted to provide a custom schema for a type that has a custom string representation (i.e., it implements fmt.Stringer). The library should ideally support this use case without causing a panic at runtime. The fact that the panic occurs not on map creation but on inspection of the ForOptions struct makes the bug particularly difficult to debug.

I discovered this when working on an MCP server: https://github.com/veggiemonk/backlog/pull/4/files#diff-10ba8694f076394780ca09af7b0f491fe66afb89144fe6f7821623b957a6c5e5R276

The ID complies with interfaces fmt.Stringer, json.Marshaler, etc...
https://github.com/veggiemonk/backlog/pull/4/files#diff-a702fe5fe5e8d999bb59813855e3e2041a66a029f4d019e3daba30d993773ed4R20-R25

I tried Initializing the slices in the struct:
https://github.com/veggiemonk/backlog/pull/4/files#diff-a702fe5fe5e8d999bb59813855e3e2041a66a029f4d019e3daba30d993773ed4R27

But it always crash.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions