Visitar URL original
Validation of uniqueItems causes panic when input is not the expected type · Issue #1042 · danielgtaylor/huma · GitHub
Skip to content

Validation of uniqueItems causes panic when input is not the expected type #1042

Closed

Description

@mheguy

Huma v2.38.0

If you have an input that is an array, and you use the uniqueItems validation, there are some paths that can lead to uncaught panics.

If the input is expecting an array of strings but you provide "myArray": [{}] this will trigger a panic.

Copilot took a look at Huma's codebase and came up with a theory that sounds cogent.

Copilot's analysis

Root cause: Huma v2's validator checks uniqueItems by hashing array elements before validating their types. When malformed input like [{}] arrives for a []string) field, the hasher panics on map[string]interface{}. Removing the tag prevents the hash attempt; huma still rejects [{}] with a proper 422 type-validation error since items must be strings.

(At this point I asked if it was a known issue)

No, it does not appear to be a reported issue. I searched the huma issue tracker for "uniqueItems panic", "unhashable", and "handleArray panic" — all returned zero results.

The bug is in handleArray in validate.go:

if s.UniqueItems {

The uniqueness check inserts items into a map[any]struct{}, which panics if an item is a non-hashable type (like map[string]interface{}). This happens when malformed JSON (e.g. [{}] for a []string field) reaches uniqueness validation before type validation rejects it.

It would be worth filing upstream. The fix on their side would be to either defer uniqueItems checking until after item-level type validation, or use a recover / reflection-based comparison for non-hashable types.

I've provided a minimal example to reproduce the issue at the bottom of the ticket.
Instructions / scenarios:

curl -v -X POST -H 'Content-Type: application/json' -d '{"arrayOfStrings": ["one", "two"]}' http://localhost:8888/demo
Expected: Returns 200 "OK"
Actual: Returns 200 "OK"

curl -v -X POST -H 'Content-Type: application/json' -d '{"arrayOfStrings": ["one", "one"]}' http://localhost:8888/demo
Expected: Returns 4xx because strings are not unique
Actual: Returns 422 because strings are not unique

curl -v -X POST -H 'Content-Type: application/json' -d '{"arrayOfStrings": [{}]}' http://localhost:8888/demo
Expected: Returns 4xx because array items are not strings
Actual: Server doesn't reply. Not even a 500.
Logs show: http: panic serving [::1]:59220: hash of unhashable type: map[string]interface {}

// ./humatest/main.go
package main

import (
	"context"
	"fmt"
	"net/http"

	"github.com/danielgtaylor/huma/v2"
	"github.com/danielgtaylor/huma/v2/adapters/humachi"
	"github.com/go-chi/chi/v5"
)

type PostInput struct {
	Body struct {
		ArrayOfStrings []string `json:"arrayOfStrings" uniqueItems:"true"`
	}
}

type Output struct {
	Body string
}

func main() {
	r := chi.NewMux()
	api := humachi.New(r, huma.DefaultConfig("Bug Demo", "1.0.0"))

	huma.Register(api, huma.Operation{
		Method: http.MethodPost,
		Path:   "/demo",
	}, func(ctx context.Context, input *PostInput) (*Output, error) {
		return &Output{Body: "OK"}, nil
	})

	fmt.Println("Listening on :8888")
	http.ListenAndServe(":8888", r)
}

If you're wondering how I found something this obscure, I was testing the API I was creating with Huma by using Schemathesis, which is a FOSS (github repo) API testing tools that I can't praise enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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