danielgtaylor / danielgtaylor/huma

feat: add machine-readable `Code` and `Params` fields to `ErrorDetail`

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

Description

## Problem

When consuming validation errors from huma, only a human-readable `message` string is available on each `ErrorDetail`. This makes it difficult to programmatically map huma errors to custom application error types without fragile string parsing.

For example, an `expected_one_of` error today looks like:

```json
{
"message": "expected value to be one of \"foo\", \"bar\"",
"location": "body.status",
"value": "baz"
}
```

To handle this in downstream code you'd have to parse the message string to
recover the allowed values — which breaks if the message format changes or is
customised via ErrorFormatter.

### Solution

Add two new omitempty fields to ErrorDetail:

- Code string — a snake_case machine-readable identifier (e.g. "expected_one_of") that corresponds to one of the new validation.Code* constants, one per existing Msg* variable.
- Params map[string]any — the constraint parameters relevant to the error, e.g. {"allowed": ["foo","bar"]} for enum errors or {"min": 3} for min-length errors.

The same error now looks like:

```json
{
"message": "expected value to be one of \"foo\", \"bar\"",
"location": "body.status",
"value": "baz",
"code": "expected_one_of",
"params": { "allowed": ["foo", "bar"] }
}
```

This enables downstream consumers to switch on Code without touching Message:

```go
for _, detail := range errModel.Errors {
switch detail.Code {
case validation.CodeExpectedOneOf:
allowed := detail.Params["allowed"].([]any)
// map to your own enum error with structured allowed values
case validation.CodeExpectedRequiredProperty:
prop := detail.Params["property"].(string)
// map to your own missing-field error
}
}
```

### Changes

- validation/messages.go — adds a Code* constant for every Msg* variable
- error.go — adds Code string and Params map[string]any to ErrorDetail (both omitempty, fully backwards-compatible)
- validate.go — adds ValidateResult.AddCode() method; migrates all internal res.Add() call sites to res.AddCode() with the appropriate code and params
- validate_test.go — adds TestValidateErrorCodes with 43 subtests covering every code and verifying params where applicable

### Backwards compatibility

- The two new fields are omitempty — existing JSON consumers see no change unless they opt in to reading the new fields.
- ValidateResult.Add() and ValidateResult.Addf() are unchanged.
- Custom NewError implementations continue to work as before.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.