kataras/iris · error

precondition failed

Error message

precondition failed

What it means

ErrPreconditionFailed is a sentinel error returned by Context.CheckPreconditions (and its helpers such as CheckIfModifiedSince / CheckIfNoneMatch) when a client precondition header does not match the server state, so the request should be answered with 412 Precondition Failed rather than the normal handler body. The library throws it to separate precondition failures from other errors like time-parsing failures of the If-Modified-Since header.

Source

Thrown at context/context.go:3458

func (ctx *Context) SetLastModified(modtime time.Time) {
	if !IsZeroTime(modtime) {
		ctx.Header(LastModifiedHeaderKey, FormatTime(ctx, modtime.UTC())) // or modtime.UTC()?
	}
}

// ErrPreconditionFailed may be returned from `Context` methods
// that has to perform one or more client side preconditions before the actual check, e.g. `CheckIfModifiedSince`.
// Usage:
// ok, err := context.CheckIfModifiedSince(modTime)
//
//	if err != nil {
//	   if errors.Is(err, context.ErrPreconditionFailed) {
//	        [handle missing client conditions,such as not valid request method...]
//	    }else {
//	        [the error is probably a time parse error...]
//	   }
//	}
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)

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Check the error with errors.Is(err, context.ErrPreconditionFailed) and reply ctx.WriteNotModified() or ctx.StatusCode(http.StatusPreconditionFailed) accordingly.
  2. Handle non-sentinel errors separately as time-parse failures of the precondition header (log/ignore and continue serving the resource).
  3. Only call CheckIfModifiedSince for GET/HEAD requests, as the method requires.
  4. Make sure the modtime/ETag you pass matches what previous responses advertised (WriteWithExpiration, HandleDir, etc.).

Example fix

// before
if err := ctx.CheckPreconditions(modtime, etag); err != nil {
    return err
}

// after
if err := ctx.CheckPreconditions(modtime, etag); err != nil {
    if errors.Is(err, context.ErrPreconditionFailed) {
        ctx.StatusCode(http.StatusPreconditionFailed)
        return nil
    }
    return err // time parse error or other
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Only run preconditions on methods that allow them:
if ctx.Method() == http.MethodGet || ctx.Method() == http.MethodHead {
    _ = ctx.CheckIfModifiedSince(modtime, context.ShouldReplyPreconditionFailed)
}

Type guard

func isPreconditionFailed(err error) bool {
    return errors.Is(err, context.ErrPreconditionFailed)
}

Try / catch

if err := ctx.CheckPreconditions(modtime, etag); err != nil {
    if errors.Is(err, context.ErrPreconditionFailed) {
        ctx.StatusCode(http.StatusPreconditionFailed)
        return nil
    }
    // otherwise it's a header time-parse error: ignore and serve normally
    return nil
}

Prevention

When it happens

Trigger: ctx.CheckIfModifiedSince(modtime, ...) when the If-Modified-Since header equals or is after modtime with ShouldReplyPreconditionFailed semantics; ctx.CheckIfNoneMatch(etag) when If-(None-)Match preconditions fail under a modifying method; ctx.CheckPreconditions(modtime, etag) returning this error so the handler must write 412.

Common situations: Implementing conditional GET caching with ETag/Last-Modified; clients (proxies, curl -H 'If-Modified-Since: ...') sending stale or mismatched conditions; HTTP method not GET/HEAD for If-Modified-Since; developers mistaking this for a time.Parse failure and not checking errors.Is(err, context.ErrPreconditionFailed) first.

Related errors


AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30). Data as JSON: /api/errors/09e22b6487627178. Report an issue: GitHub.