danielgtaylor / danielgtaylor/huma
feat: add machine-readable `Code` and `Params` fields to `ErrorDetail`
- 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.