danielgtaylor / danielgtaylor/huma

Validation for string-based types in Enum has unexpected results

Open
#600 2 comments 0 reactions 0 assignees View on GitHub
question
Dominant language
Go
Stars
4.4k
Forks
285
Avg merge
40m
Merged PRs (30d)
1

Description

Hi there,

I have the following types

**main.go**

```go
const (
NameTypeFoo NameType = "FOO"
NameTypeBar NameType = "BAR"
)

type (
NameType string
UserCreateRequest struct {
Name NameType `json:"name" minLength:"1" maxLength:"32"`
}

CreateUserInput struct {
Body UserCreateRequest
}

CreateUserOutput struct {
Body struct {
Message string `json:"message"`
}
}
)

var _ huma.SchemaTransformer = NameType("")

func (nt NameType) TransformSchema(r huma.Registry, s *huma.Schema) *huma.Schema {
s.Enum = []interface{}{NameTypeFoo, NameTypeBar}
s.Default = NameTypeFoo
s.PrecomputeMessages()
return s
}

func addRoutes(api huma.API) {
huma.Register(api, huma.Operation{
OperationID: "CreateUser",
Method: http.MethodPost,
Path: "/user",
}, func(ctx context.Context, input *CreateUserInput) (*CreateUserOutput, error) {
resp := &CreateUserOutput{}
resp.Body.Message = "CreateUser works!"
return resp, nil
})
}
```

I have two string-type-based constants: `NameTypeFoo` and `NameTypeBar` ( `FOO` and `BAR` respectively ).

I set `Enum` in `TransformSchema` by `s.Enum = []interface{}{NameTypeFoo, NameTypeBar}`.

After that, I get the correct OAS

spec

```yaml
components:
schemas:
CreateUserOutputBody:
additionalProperties: false
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- https://example.com/schemas/CreateUserOutputBody.json
format: uri
readOnly: true
type: string
message:
type: string
required:
- message
type: object
ErrorDetail:
additionalProperties: false
properties:
location:
description: Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'
type: string
message:
description: Error message text
type: string
value:
description: The value at the given location
type: object
ErrorModel:
additionalProperties: false
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- https://example.com/schemas/ErrorModel.json
format: uri
readOnly: true
type: string
detail:
description: A human-readable explanation specific to this occurrence of the problem.
examples:
- Property foo is required but is missing.
type: string
errors:
description: Optional list of individual error details
items:
$ref: "#/components/schemas/ErrorDetail"
type:
- array
- "null"
instance:
description: A URI reference that identifies the specific occurrence of the problem.
examples:
- https://example.com/error-log/abc123
format: uri
type: string
status:
description: HTTP status code
examples:
- 400
format: int64
type: integer
title:
description: A short, human-readable summary of the problem type. This value should not change between occurrences of the error.
examples:
- Bad Request
type: string
type:
default: about:blank
description: A URI reference to human-readable documentation for the error.
examples:
- https://example.com/errors/example
format: uri
type: string
type: object
UserCreateRequest:
additionalProperties: false
properties:
$schema:
description: A URL to the JSON Schema for this object.
examples:
- https://example.com/schemas/UserCreateRequest.json
format: uri
readOnly: true
type: string
name:
default: FOO
enum:
- FOO
- BAR
maxLength: 32
minLength: 1
type: string
required:
- name
type: object
info:
title: My API
version: 1.0.0
openapi: 3.1.0
paths:
/user:
post:
operationId: CreateUser
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/UserCreateRequest"
required: true
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/CreateUserOutputBody"
description: OK
default:
content:
application/problem+json:
schema:
$ref: "#/components/schemas/ErrorModel"
description: Error
```

where `enum` is correct

```yaml
name:
default: FOO
enum:
- FOO
- BAR
maxLength: 32
minLength: 1
type: string
```

I expect the request with `FOO` or `BAR` to pass the validation.

**The issue**

main_test.go

```go
func TestUserCreate(t *testing.T) {
_, api := humatest.New(t, huma.DefaultConfig("Test API", "1.0.0"))
addRoutes(api)

_ = api.Post("/user", map[string]any{"name": "FOO"})
}
```

I get a `422` error with the correct `FOO` ( or `BAR` ) value on the request.

```
main_test.go:14: Making request:
POST /user HTTP/1.1
Content-Type: application/json

{
"name": "FOO"
}
main_test.go:14: Got response:
HTTP/1.1 422 Unprocessable Entity
Connection: close
Content-Type: application/problem+json

{
"$schema": "https:///schemas/ErrorModel.json",
"title": "Unprocessable Entity",
"status": 422,
"detail": "validation failed",
"errors": [
{
"message": "expected value to be one of \"FOO, BAR\"",
"location": "body.name",
"value": "FOO"
}
]
}
--- PASS: TestUserCreate (0.00s)
```

My question is it a bug or a wrong way to set `Enum` in `TransformSchema`?
If I set it as strings `s.Enum = []interface{}{"FOO", "BAR"}` everything works as expected.

Thank you.

Contributor guide

No contributing guide indexed for this repository

Research direction

Reproduce the failure from main_test.go using humatest.New and api.Post, then trace how TransformSchema and s.Enum reach request validation. Compare named string values with plain strings; done means valid FOO and BAR requests pass while the generated OpenAPI enum remains correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, openapi
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.