{"record":{"id":"1bedd4de3c644cb6","repo":"juicedata/juicefs","slug":"mtime-deviation-is-too-large-the-actual-mtime-is","errorCode":null,"errorMessage":"mtime deviation is too large, the actual mtime is %s but got %s","messagePattern":"mtime deviation is too large, the actual mtime is (.+?) but got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cmd/objbench.go","lineNumber":1137,"sourceCode":"\t\t\treturn fmt.Errorf(\"failed to head object %s\", err)\n\t\t} else if info, ok := objInfo.(object.File); ok {\n\t\t\tif info.Mode()&0xFFF != 0777 {\n\t\t\t\treturn fmt.Errorf(\"expect mode %o but got %o\", 0777, info.Mode())\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\n\tfunFSCase(\"change mtime\", func() error {\n\t\tmtime := time.Now().Add(-10 * time.Minute)\n\t\tif err := fi.Chtimes(key, mtime); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to chtimes %s\", err)\n\t\t}\n\t\tif objInfo, err := blob.Head(ctx, key); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to head object %s\", err)\n\t\t} else {\n\t\t\tif objInfo.Mtime().Before(mtime.Add(-2*time.Second)) || objInfo.Mtime().After(mtime.Add(2*time.Second)) {\n\t\t\t\treturn fmt.Errorf(\"mtime deviation is too large, the actual mtime is %s but got %s\", mtime.Format(time.RFC3339), objInfo.Mtime().Format(time.RFC3339))\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n}\n","sourceCodeStart":1119,"sourceCodeEnd":1143,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/cmd/objbench.go#L1119-L1143","documentation":"objbench verifies that Chtimes took effect within a ±2 second tolerance: if the mtime returned by Head is before mtime-2s or after mtime+2s, this error fires. It means the backend did not store (or returned with drift) the requested modification time.","triggerScenarios":"fi.Chtimes(key, now-10min) succeeds, Head returns objInfo.Mtime() outside [mtime-2s, mtime+2s] — e.g. backend ignores Chtimes and reports upload time, or clock skew between client and server timestamps.","commonSituations":"Backends that silently ignore SetMtime; systems with large NTP clock skew; stores that round or truncate mtimes to whole seconds/minutes; eventual-consistency returning stale metadata.","solutions":["Use a backend that persists custom mtime metadata (e.g. stores mtime as object metadata like x-amz-meta-mtime).","Sync clocks (NTP) between client and storage nodes.","Rerun Head after a short delay to rule out eventual-consistency staleness.","Accept reduced precision on backends with second-granularity mtimes; treat ±2s tolerance failures as backend limitation."],"exampleFix":"// before\nmtime := time.Now().Add(-10 * time.Minute)\nif err := fi.Chtimes(key, mtime); err != nil {\n// after: verify backend supports Chtimes first, and log returned mtime\nmtime := time.Now().Add(-10 * time.Minute).Truncate(time.Second)\nif err := fi.Chtimes(key, mtime); err != nil {","handlingStrategy":"validation","validationCode":"if !backend.SupportsSetMtime() {\n    return nil // skip mtime verification\n}","typeGuard":null,"tryCatchPattern":"if objInfo.Mtime().Before(mtime.Add(-2*time.Second)) || objInfo.Mtime().After(mtime.Add(2*time.Second)) {\n    log.Warnf(\"mtime drift on %s: want %s got %s\", key, mtime, objInfo.Mtime())\n    return nil\n}","preventionTips":["Use backends that persist mtime metadata (e.g. x-amz-meta-mtime style).","Keep client clocks NTP-synced to avoid skew.","Truncate target mtime to whole seconds on second-granularity stores.","Allow a retry delay for eventually-consistent metadata."],"tags":["object-storage","mtime","clock-skew","benchmark"],"backgroundTag":"value-out-of-range","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}