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

  1. Inspect the wrapped root cause with errors.Cause/is and fix the underlying Attributes failure (credentials, network, object existence).
  2. Retry the GetRange; transient objectstore errors often clear on retry.
  3. Verify the object exists and the bucket client has permission to HEAD objects.
  4. 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

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)