{"record":{"id":"b059a30dc6f8c704","repo":"thanos-io/thanos","slug":"fetching-range-d-d","errorCode":null,"errorMessage":"fetching range [%d, %d]","messagePattern":"fetching range \\[(.+?), (.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/store/cache/caching_bucket.go","lineNumber":428,"sourceCode":"\t\t\tmissing = append(missing, rng{start: off, end: off + cfg.SubrangeSize})\n\t\t}\n\t}\n\n\tmissing = mergeRanges(missing, 0) // Merge adjacent ranges.\n\t// Keep merging until we have only max number of ranges (= requests).\n\tfor limit := cfg.SubrangeSize; cfg.MaxSubRequests > 0 && len(missing) > cfg.MaxSubRequests; limit = limit * 2 {\n\t\tmissing = mergeRanges(missing, limit)\n\t}\n\n\tvar hitsMutex sync.Mutex\n\n\t// Run parallel queries for each missing range. Fetched data is stored into 'hits' map, protected by hitsMutex.\n\tg, gctx := errgroup.WithContext(ctx)\n\tfor _, m := range missing {\n\t\tg.Go(func() error {\n\t\t\tr, err := cb.Bucket.GetRange(gctx, name, m.start, m.end-m.start)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.Wrapf(err, \"fetching range [%d, %d]\", m.start, m.end)\n\t\t\t}\n\t\t\tdefer runutil.CloseWithLogOnErr(cb.logger, r, \"fetching range [%d, %d]\", m.start, m.end)\n\n\t\t\tvar bufSize int64\n\t\t\tif lastSubrangeOffset >= m.end {\n\t\t\t\tbufSize = m.end - m.start\n\t\t\t} else {\n\t\t\t\tbufSize = ((m.end - m.start) - cfg.SubrangeSize) + int64(lastSubrangeLength)\n\t\t\t}\n\n\t\t\tbuf := make([]byte, bufSize)\n\t\t\t_, err = io.ReadFull(r, buf)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.Wrapf(err, \"fetching range [%d, %d]\", m.start, m.end)\n\t\t\t}\n\n\t\t\tfor off := m.start; off < m.end && gctx.Err() == nil; off += cfg.SubrangeSize {\n\t\t\t\tkey := cacheKeys[off]","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/store/cache/caching_bucket.go#L410-L446","documentation":"Inside the errgroup in cachedGetRange, each missing subrange is fetched directly from the underlying bucket with Bucket.GetRange. If that call fails, the error is wrapped with \"fetching range [start, end]\" identifying the failing byte range. This is the parallel-fetch phase that backfills cache misses.","triggerScenarios":"CachingBucket.GetRange with cache misses triggers concurrent Bucket.GetRange calls; any underlying read error (objectstore 5xx, timeout, object deleted mid-read, ctx cancellation) is wrapped for the specific missing range.","commonSituations":"Objectstore throttling/limits when many ranges are fetched in parallel; large index-header or meta files fetched range-wise over flaky networks; query cancellation aborting the errgroup context.","solutions":["Read the wrapped cause to find the underlying GetRange failure and fix it (network, credentials, objectstore limits).","Retry; errgroup cancels siblings on first error so a transient blip fails the whole fetch — rerun the query.","Reduce parallelism / tune GetRange caching config (subrange size, TTL) to lower load on the object store.","Check gctx cancellation: if the caller canceled, the error is expected and should be ignored/logged at debug level."],"exampleFix":"// before\nr, err := cb.GetRange(ctx, name, off, length)\nif err != nil {\n    return errors.Wrapf(err, \"fetching range [%d, %d]\", off, off+length)\n}\n// after\nr, err := cb.GetRange(ctx, name, off, length)\nif err != nil {\n    if ctx.Err() != nil {\n        return nil // caller canceled; don't fail on wrapped range fetch\n    }\n    return errors.Wrapf(err, \"fetching range [%d, %d]\", off, off+length)\n}","handlingStrategy":"retry","validationCode":"// Verify object reachability and size before range fan-out\nattrs, err := bkt.Attributes(ctx, name)\nif err != nil || off+length > attrs.Size {\n    return errors.New(\"range out of bounds or object unreachable\")\n}","typeGuard":null,"tryCatchPattern":"err := fetchWithRetry(ctx, 3, func() error {\n    _, err := cb.GetRange(ctx, name, off, length)\n    return err\n})\nif err != nil {\n    if ctx.Err() != nil { return nil } // canceled\n    return errors.Wrapf(err, \"fetching range [%d, %d]\", off, off+length)\n}","preventionTips":["Retry transient range-fetch failures; errgroup cancels siblings so rerun the whole fetch","Respect objectstore rate limits; tune subrange size/parallelism","Increase LB/proxy timeouts for long range reads","Distinguish caller cancellation (ctx.Err) from real fetch errors"],"tags":["go","object-store","network","range-request"],"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"}