Repository navigation
Description
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:
Line 610 in 2b69054
| 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.
Activity
perf(validate): keep uniqueItems check linear for hashable items
fix: prevent panic in uniqueItems validation for unhashable types (da…