thanos-io/thanos · error
failed to get object attributes
Error message
failed to get object attributes: %s
What it means
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>".
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).
Example fix
// before
rc, err := cb.GetRange(ctx, name, 0, size)
if err != nil {
return err
}
// after
rc, err := cb.GetRange(ctx, name, 0, size)
if err != nil {
if cb.IsObjNotFoundErr(errors.Cause(err)) {
return nil, nil // object gone
}
return errors.Wrapf(err, "get range %s", name)
} Defensive patterns
Strategy: try-catch
Validate before calling
_, err := bkt.Attributes(ctx, name)
if err != nil {
return errors.Wrapf(err, "object %s not reachable before GetRange", name)
} Type guard
func isNotFoundCause(err error, cb *cache.CachingBucket) bool {
return cb.IsObjNotFoundErr(errors.Cause(err))
} Try / catch
rc, err := cb.GetRange(ctx, name, off, length)
if err != nil {
switch {
case cb.IsObjNotFoundErr(errors.Cause(err)):
return nil, nil
case isRetryable(errors.Cause(err)):
return retry(ctx)
default:
return errors.Wrapf(err, "get range %s", name)
}
} Prevention
- 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)
When it happens
Trigger: 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).
Common situations: Objectstore credentials/permissions lacking s3:GetObject on the head request; transient objectstore outages; object deleted between calls; misconfigured attributes cache backend.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/668197feb14dd9db.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/store/cache/caching_bucket.go:337
return objstore.ObjectAttributes{}, err
}
if raw, err := json.Marshal(attrs); err == nil {
cache.Store(map[string][]byte{key: raw}, ttl)
} else {
level.Warn(cb.logger).Log("msg", "failed to encode cached Attributes result", "key", key, "err", err)
}
return attrs, nil
}
func (cb *CachingBucket) cachedGetRange(ctx context.Context, name string, offset, length int64, cfgName string, cfg *cache.GetRangeConfig) (io.ReadCloser, error) {
cb.operationRequests.WithLabelValues(objstore.OpGetRange, cfgName).Inc()
cb.requestedGetRangeBytes.WithLabelValues(cfgName).Add(float64(length))
attrs, err := cb.cachedAttributes(ctx, name, cfgName, cfg.Cache, cfg.AttributesTTL)
if err != nil {
return nil, errors.Wrapf(err, "failed to get object attributes: %s", name)
}
// If length goes over object size, adjust length. We use it later to limit number of read bytes.
if offset+length > attrs.Size {
length = attrs.Size - offset
}
// Start and end range are subrange-aligned offsets into object, that we're going to read.
startRange := (offset / cfg.SubrangeSize) * cfg.SubrangeSize
endRange := ((offset + length) / cfg.SubrangeSize) * cfg.SubrangeSize
if (offset+length)%cfg.SubrangeSize > 0 {
endRange += cfg.SubrangeSize
}
// The very last subrange in the object may have length that is not divisible by subrange size.
lastSubrangeOffset := endRange - cfg.SubrangeSize
lastSubrangeLength := int(cfg.SubrangeSize)
if endRange > attrs.Size {View on GitHub (pinned to 35b8b99117)