{"record":{"id":"668197feb14dd9db","repo":"thanos-io/thanos","slug":"failed-to-get-object-attributes-s","errorCode":null,"errorMessage":"failed to get object attributes: %s","messagePattern":"failed to get object attributes: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/store/cache/caching_bucket.go","lineNumber":337,"sourceCode":"\t\treturn objstore.ObjectAttributes{}, err\n\t}\n\n\tif raw, err := json.Marshal(attrs); err == nil {\n\t\tcache.Store(map[string][]byte{key: raw}, ttl)\n\t} else {\n\t\tlevel.Warn(cb.logger).Log(\"msg\", \"failed to encode cached Attributes result\", \"key\", key, \"err\", err)\n\t}\n\n\treturn attrs, nil\n}\n\nfunc (cb *CachingBucket) cachedGetRange(ctx context.Context, name string, offset, length int64, cfgName string, cfg *cache.GetRangeConfig) (io.ReadCloser, error) {\n\tcb.operationRequests.WithLabelValues(objstore.OpGetRange, cfgName).Inc()\n\tcb.requestedGetRangeBytes.WithLabelValues(cfgName).Add(float64(length))\n\n\tattrs, err := cb.cachedAttributes(ctx, name, cfgName, cfg.Cache, cfg.AttributesTTL)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed to get object attributes: %s\", name)\n\t}\n\n\t// If length goes over object size, adjust length. We use it later to limit number of read bytes.\n\tif offset+length > attrs.Size {\n\t\tlength = attrs.Size - offset\n\t}\n\n\t// Start and end range are subrange-aligned offsets into object, that we're going to read.\n\tstartRange := (offset / cfg.SubrangeSize) * cfg.SubrangeSize\n\tendRange := ((offset + length) / cfg.SubrangeSize) * cfg.SubrangeSize\n\tif (offset+length)%cfg.SubrangeSize > 0 {\n\t\tendRange += cfg.SubrangeSize\n\t}\n\n\t// The very last subrange in the object may have length that is not divisible by subrange size.\n\tlastSubrangeOffset := endRange - cfg.SubrangeSize\n\tlastSubrangeLength := int(cfg.SubrangeSize)\n\tif endRange > attrs.Size {","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/store/cache/caching_bucket.go#L319-L355","documentation":"cachedGetRange first fetches cached object attributes (size) to know the object's length; if cachedAttributes fails (cache lookup failure or underlying Bucket.Attributes error), the GetRange cannot proceed and the error is wrapped as \"failed to get object attributes: <name>\".","triggerScenarios":"Calling CachingBucket.GetRange when the attributes cache misses and the underlying Bucket.Attributes call fails (network error, permission denied, object actually missing, or the attributes-cache store itself erroring).","commonSituations":"Objectstore credentials/permissions lacking s3:GetObject on the head request; transient objectstore outages; object deleted between calls; misconfigured attributes cache backend.","solutions":["Inspect the wrapped root cause with errors.Cause/is and fix the underlying Attributes failure (credentials, network, object existence).","Retry the GetRange; transient objectstore errors often clear on retry.","Verify the object exists and the bucket client has permission to HEAD objects.","If the attributes cache backend fails, check its connectivity/health metrics (caching_bucket operations)."],"exampleFix":"// before\nrc, err := cb.GetRange(ctx, name, 0, size)\nif err != nil {\n    return err\n}\n// after\nrc, err := cb.GetRange(ctx, name, 0, size)\nif err != nil {\n    if cb.IsObjNotFoundErr(errors.Cause(err)) {\n        return nil, nil // object gone\n    }\n    return errors.Wrapf(err, \"get range %s\", name)\n}","handlingStrategy":"try-catch","validationCode":"_, err := bkt.Attributes(ctx, name)\nif err != nil {\n    return errors.Wrapf(err, \"object %s not reachable before GetRange\", name)\n}","typeGuard":"func isNotFoundCause(err error, cb *cache.CachingBucket) bool {\n    return cb.IsObjNotFoundErr(errors.Cause(err))\n}","tryCatchPattern":"rc, err := cb.GetRange(ctx, name, off, length)\nif err != nil {\n    switch {\n    case cb.IsObjNotFoundErr(errors.Cause(err)):\n        return nil, nil\n    case isRetryable(errors.Cause(err)):\n        return retry(ctx)\n    default:\n        return errors.Wrapf(err, \"get range %s\", name)\n    }\n}","preventionTips":["Verify objectstore credentials allow HEAD/GetObject operations","Retry transient attribute errors with backoff","Check attributes-cache backend health metrics","Ensure objects are immutable during reads (no mid-read deletion)"],"tags":["go","object-store","cache","network"],"backgroundTag":"http-request-failed","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}