{"record":{"id":"a427419368a883eb","repo":"thanos-io/thanos","slug":"close-reader","errorCode":null,"errorMessage":"close reader","messagePattern":"close reader","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/block/indexheader/binary_reader.go","lineNumber":200,"sourceCode":"\tindexFilepath := filepath.Join(id.String(), block.IndexFilename)\n\tattrs, err := bkt.Attributes(ctx, indexFilepath)\n\tif err != nil {\n\t\treturn nil, 0, errors.Wrapf(err, \"get object attributes of %s\", indexFilepath)\n\t}\n\n\trc, err := bkt.GetRange(ctx, indexFilepath, 0, index.HeaderLen)\n\tif err != nil {\n\t\treturn nil, 0, errors.Wrapf(err, \"get TOC from object storage of %s\", indexFilepath)\n\t}\n\n\tb, err := io.ReadAll(rc)\n\tif err != nil {\n\t\trunutil.CloseWithErrCapture(&err, rc, \"close reader\")\n\t\treturn nil, 0, errors.Wrapf(err, \"get header from object storage of %s\", indexFilepath)\n\t}\n\n\tif err := rc.Close(); err != nil {\n\t\treturn nil, 0, errors.Wrap(err, \"close reader\")\n\t}\n\n\tif m := binary.BigEndian.Uint32(b[0:4]); m != index.MagicIndex {\n\t\treturn nil, 0, errors.Errorf(\"invalid magic number %x for %s\", m, indexFilepath)\n\t}\n\n\tversion := int(b[4:5][0])\n\n\tif version != index.FormatV1 && version != index.FormatV2 {\n\t\treturn nil, 0, errors.Errorf(\"not supported index file version %d of %s\", version, indexFilepath)\n\t}\n\n\tir := &chunkedIndexReader{\n\t\tctx:  ctx,\n\t\tpath: indexFilepath,\n\t\tsize: uint64(attrs.Size),\n\t\tbkt:  bkt,\n\t}","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/block/indexheader/binary_reader.go#L182-L218","documentation":"After successfully reading the header bytes, the code closes the object-storage range reader and wraps a non-nil rc.Close() error as \"close reader\". A Close failure here means the HTTP response body could not be cleanly drained/closed (connection teardown error); the header data itself was read fine.","triggerScenarios":"WriteBinary -> newChunkedIndexReader -> rc.Close() returning an error when closing the ranged GET response body: underlying connection reset during close, provider client returning close-time errors (e.g. some s3/gcs client wrappers surface EOF here), or double-close on a wrapped reader.","commonSituations":"Network blip exactly at connection teardown; custom objstore wrappers whose Close propagates body-read leftovers as errors; aggressive connection reuse with broken keep-alive sockets.","solutions":["Retry the whole WriteBinary — the data was read successfully and this is a teardown-only failure.","Tune keep-alive/idle-connection settings in the objstore HTTP config to avoid reset sockets.","If a custom objstore.BucketReader wrapper is in use, ensure Close only surfaces genuine close errors and drains the body first.","If this error appears persistently with one provider, update the object-store client/Thanos version for known close-error handling fixes."],"exampleFix":"// before: fail on any close error\nif err := rc.Close(); err != nil {\n    return nil, 0, errors.Wrap(err, \"close reader\")\n}\n// after (caller side): treat close failure as non-fatal when payload already read\nif err := rc.Close(); err != nil {\n    log.Warn(\"index header reader close failed (data already read)\", \"err\", err)\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Go: header bytes are already in memory, so a close error can often be tolerated\nif err := rebuild(); err != nil {\n    if strings.Contains(err.Error(), \"close reader\") {\n        log.Warn(\"non-fatal close error after reading index header; continuing\", \"err\", err)\n        return nil // fallback: data was fully read\n    }\n    return err\n}","preventionTips":["Ensure objstore response bodies are fully drained (io.ReadAll) before Close to avoid reset sockets.","Keep object-store client libraries and Thanos updated for close-error handling fixes.","Tune keep-alive settings to reduce stale-socket resets at teardown.","If you wrap objstore.BucketReader, never surface double-close as an error to callers."],"tags":["object-storage","io","close","thanos"],"backgroundTag":"broken-pipe","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"}