kataras/iris · warning
ErrPreconditionFailed
ErrPreconditionFailed
Error message
method: %w
What it means
Returned by Context.CheckIfModifiedSince when the request method is neither GET nor HEAD, since conditional headers like If-Modified-Since are only defined for safe retrieval methods. The library wraps the sentinel ErrPreconditionFailed so callers can detect that caching logic was skipped due to a method precondition rather than a header parsing problem.
Source
Thrown at context/context.go:3476
var ErrPreconditionFailed = errors.New("precondition failed")
// CheckIfModifiedSince checks if the response is modified since the "modtime".
// Note that it has nothing to do with server-side caching.
// It does those checks by checking if the "If-Modified-Since" request header
// sent by client or a previous server response header
// (e.g with WriteWithExpiration or HandleDir or Favicon etc.)
// is a valid one and it's before the "modtime".
//
// A check for !modtime && err == nil is necessary to make sure that
// it's not modified since, because it may return false but without even
// had the chance to check the client-side (request) header due to some errors,
// like the HTTP Method is not "GET" or "HEAD" or if the "modtime" is zero
// or if parsing time from the header failed. See `ErrPreconditionFailed` too.
//
// It's mostly used internally, e.g. `context#WriteWithExpiration`.
func (ctx *Context) CheckIfModifiedSince(modtime time.Time) (bool, error) {
if method := ctx.Method(); method != http.MethodGet && method != http.MethodHead {
return false, fmt.Errorf("method: %w", ErrPreconditionFailed)
}
ims := ctx.GetHeader(IfModifiedSinceHeaderKey)
if ims == "" || IsZeroTime(modtime) {
return false, fmt.Errorf("zero time: %w", ErrPreconditionFailed)
}
t, err := ParseTime(ctx, ims)
if err != nil {
return false, err
}
// sub-second precision, so
// use mtime < t+1s instead of mtime <= t to check for unmodified.
if modtime.UTC().Before(t.Add(1 * time.Second)) {
return false, nil
}
return true, nil
}
// WriteNotModified sends a 304 "Not Modified" status code to the client,View on GitHub (pinned to 7bedaf55a0)
Solutions
- Only call CheckIfModifiedSince when ctx.Method() is GET or HEAD; short-circuit otherwise.
- Split handlers per method so caching logic runs only on retrieval routes.
- Check errors.Is(err, context.ErrPreconditionFailed) and skip caching rather than failing the request.
- For non-GET caching, use ETag/If-None-Match semantics manually.
Example fix
// before
if modified, err := ctx.CheckIfModifiedSince(modtime); err != nil { return err } else if !modified { ctx.NotModified(); return nil }
// after
if m := ctx.Method(); m == http.MethodGet || m == http.MethodHead {
if modified, err := ctx.CheckIfModifiedSince(modtime); err == nil && !modified {
ctx.NotModified()
return nil
}
} Defensive patterns
Strategy: validation
Validate before calling
m := ctx.Method()
if m != http.MethodGet && m != http.MethodHead {
// skip conditional caching entirely
} Type guard
func cacheableMethod(ctx *context.Context) bool {
m := ctx.Method()
return m == http.MethodGet || m == http.MethodHead
} Try / catch
modified, err := ctx.CheckIfModifiedSince(modtime)
if errors.Is(err, context.ErrPreconditionFailed) {
// not GET/HEAD or no header: serve full response, ignore
} else if err != nil {
return err
} else if !modified {
ctx.NotModified()
return nil
} Prevention
- Register conditional-caching handlers only on GET/HEAD routes
- Always handle ErrPreconditionFailed as 'skip caching', never as a 500
- Use app.Get/app.Head instead of app.Any when caching is involved
When it happens
Trigger: Calling ctx.CheckIfModifiedSince(modtime) inside a handler that also serves POST/PUT/DELETE/PATCH requests, or from WriteWithExpiration on a non-GET/HEAD route.
Common situations: Shared handler registered for multiple methods (e.g. app.Any) doing conditional caching; POST responses with cache semantics; method-agnostic middleware.
Related errors
- precondition failed
- ErrEmptyFormField
- dest directory: %s: eval symlinks: %w
- ErrNotFound
- multipart related: body copy because of iris.Configuration.D
AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30).
Data as JSON: /api/errors/15241f802af7dce9.
Report an issue: GitHub.