{"record":{"id":"15241f802af7dce9","repo":"kataras/iris","slug":"errpreconditionfailed","errorCode":"ErrPreconditionFailed","errorMessage":"method: %w","messagePattern":"method: %w","errorType":"error_code","errorClass":null,"httpStatus":412,"severity":"warning","filePath":"context/context.go","lineNumber":3476,"sourceCode":"var ErrPreconditionFailed = errors.New(\"precondition failed\")\n\n// CheckIfModifiedSince checks if the response is modified since the \"modtime\".\n// Note that it has nothing to do with server-side caching.\n// It does those checks by checking if the \"If-Modified-Since\" request header\n// sent by client or a previous server response header\n// (e.g with WriteWithExpiration or HandleDir or Favicon etc.)\n// is a valid one and it's before the \"modtime\".\n//\n// A check for !modtime && err == nil is necessary to make sure that\n// it's not modified since, because it may return false but without even\n// had the chance to check the client-side (request) header due to some errors,\n// like the HTTP Method is not \"GET\" or \"HEAD\" or if the \"modtime\" is zero\n// or if parsing time from the header failed. See `ErrPreconditionFailed` too.\n//\n// It's mostly used internally, e.g. `context#WriteWithExpiration`.\nfunc (ctx *Context) CheckIfModifiedSince(modtime time.Time) (bool, error) {\n\tif method := ctx.Method(); method != http.MethodGet && method != http.MethodHead {\n\t\treturn false, fmt.Errorf(\"method: %w\", ErrPreconditionFailed)\n\t}\n\tims := ctx.GetHeader(IfModifiedSinceHeaderKey)\n\tif ims == \"\" || IsZeroTime(modtime) {\n\t\treturn false, fmt.Errorf(\"zero time: %w\", ErrPreconditionFailed)\n\t}\n\tt, err := ParseTime(ctx, ims)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\t// sub-second precision, so\n\t// use mtime < t+1s instead of mtime <= t to check for unmodified.\n\tif modtime.UTC().Before(t.Add(1 * time.Second)) {\n\t\treturn false, nil\n\t}\n\treturn true, nil\n}\n\n// WriteNotModified sends a 304 \"Not Modified\" status code to the client,","sourceCodeStart":3458,"sourceCodeEnd":3494,"githubUrl":"https://github.com/kataras/iris/blob/7bedaf55a0b64bbb2248a5845a2c60d81a30996a/context/context.go#L3458-L3494","documentation":"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.","triggerScenarios":"Calling ctx.CheckIfModifiedSince(modtime) inside a handler that also serves POST/PUT/DELETE/PATCH requests, or from WriteWithExpiration on a non-GET/HEAD route.","commonSituations":"Shared handler registered for multiple methods (e.g. app.Any) doing conditional caching; POST responses with cache semantics; method-agnostic middleware.","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."],"exampleFix":"// before\nif modified, err := ctx.CheckIfModifiedSince(modtime); err != nil { return err } else if !modified { ctx.NotModified(); return nil }\n// after\nif m := ctx.Method(); m == http.MethodGet || m == http.MethodHead {\n    if modified, err := ctx.CheckIfModifiedSince(modtime); err == nil && !modified {\n        ctx.NotModified()\n        return nil\n    }\n}","handlingStrategy":"validation","validationCode":"m := ctx.Method()\nif m != http.MethodGet && m != http.MethodHead {\n    // skip conditional caching entirely\n}","typeGuard":"func cacheableMethod(ctx *context.Context) bool {\n    m := ctx.Method()\n    return m == http.MethodGet || m == http.MethodHead\n}","tryCatchPattern":"modified, err := ctx.CheckIfModifiedSince(modtime)\nif errors.Is(err, context.ErrPreconditionFailed) {\n    // not GET/HEAD or no header: serve full response, ignore\n} else if err != nil {\n    return err\n} else if !modified {\n    ctx.NotModified()\n    return nil\n}","preventionTips":["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"],"tags":["go","iris","http-caching","conditional-request"],"backgroundTag":"conditional-request-precondition-failed","analyzedSha":"7bedaf55a0b64bbb2248a5845a2c60d81a30996a","analyzedAt":"2026-08-30T20:38:16.250Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}