kataras/iris · error
ErrNotFound
ErrNotFound
Error message
unmarshal: empty body: %w
What it means
Returned by Context.UnmarshalBody (the engine behind ctx.ReadJSON, ReadXML and custom Unmarshaler reads) when the request has no body at all (ctx.request.Body == nil). The library wraps the sentinel ErrNotFound, so callers can distinguish 'no body to unmarshal' from a decoding failure.
Source
Thrown at context/context.go:2707
return body, nil
}
// Validator is the validator for request body on Context methods such as
// ReadJSON, ReadMsgPack, ReadXML, ReadYAML, ReadForm, ReadQuery, ReadBody and e.t.c.
type Validator interface {
Struct(any) error
// If community asks for more than a struct validation on JSON, XML, MsgPack, Form, Query and e.t.c
// then we should add more methods here, alternative approach would be to have a
// `Validator:Validate(any) error` and a map[reflect.Kind]Validator instead.
}
// UnmarshalBody reads the request's body and binds it to a value or pointer of any type
// Examples of usage: context.ReadJSON, context.ReadXML.
//
// Example: https://github.com/kataras/iris/blob/main/_examples/request-body/read-custom-via-unmarshaler/main.go
func (ctx *Context) UnmarshalBody(outPtr any, unmarshaler Unmarshaler) error {
if ctx.request.Body == nil {
return fmt.Errorf("unmarshal: empty body: %w", ErrNotFound)
}
rawData, err := ctx.GetBody()
if err != nil {
return err
}
if decoderWithCtx, ok := outPtr.(BodyDecoderWithContext); ok {
return decoderWithCtx.DecodeContext(ctx.request.Context(), rawData)
}
// check if the v contains its own decode
// in this case the v should be a pointer also,
// but this is up to the user's custom Decode implementation*
//
// See 'BodyDecoder' for more.
if decoder, isDecoder := outPtr.(BodyDecoder); isDecoder {
return decoder.Decode(rawData)View on GitHub (pinned to 7bedaf55a0)
Solutions
- Have clients always send a valid body for endpoints using ReadJSON/ReadXML (e.g. '{}').
- Check errors.Is(err, context.ErrNotFound) and return 400 'empty request body'.
- Use ctx.GetBody() once and decode manually if you need custom empty-body defaults.
- In tests, set req.Body = io.NopCloser(strings.NewReader(...)) instead of nil.
Example fix
// before
var p Product
if err := ctx.ReadJSON(&p); err != nil { return err }
// after
var p Product
if err := ctx.ReadJSON(&p); err != nil {
if errors.Is(err, context.ErrNotFound) {
ctx.StatusCode(iris.StatusBadRequest)
return errors.New("request body is required")
}
return err
} Defensive patterns
Strategy: validation
Validate before calling
if ctx.Request().ContentLength == 0 {
return errors.New("request body is required")
} Type guard
func hasRequestBody(ctx *context.Context) bool {
return ctx.Request().Body != nil && ctx.Request().ContentLength != 0
} Try / catch
var p Payload
if err := ctx.ReadJSON(&p); err != nil {
if errors.Is(err, context.ErrNotFound) {
ctx.StatusCode(iris.StatusBadRequest)
return errors.New("empty request body")
}
return err
} Prevention
- Require clients to send a body (even '{}') on POST/PUT endpoints that decode payloads
- Guard ContentLength == 0 before ReadJSON/ReadXML
- In tests always set a non-nil req.Body
When it happens
Trigger: POST/PUT requests with Content-Length 0 or no body calling ctx.ReadJSON/ReadXML/UnmarshalBody; requests where a prior handler already consumed and closed the body.
Common situations: Clients sending POST with empty payload (e.g. fetch without body on POST); middleware that consumed the body; tests forgetting to set a request body; curl -X POST without -d.
Related errors
- empty form
- ErrEmptyFormField
- multipart related: body copy because of iris.Configuration.D
- %s: %w
- errors joined from param parser: strings.Join(p.errors, "\n"
AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30).
Data as JSON: /api/errors/417d5a594763a07d.
Report an issue: GitHub.