{"record":{"id":"f82ca7491858987f","repo":"thanos-io/thanos","slug":"close-toc-reader","errorCode":null,"errorMessage":"close toc reader","messagePattern":"close toc reader","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/block/indexheader/binary_reader.go","lineNumber":242,"sourceCode":"\tir.toc = toc\n\n\treturn ir, version, nil\n}\n\nfunc (r *chunkedIndexReader) readTOC() (*index.TOC, error) {\n\trc, err := r.bkt.GetRange(r.ctx, r.path, int64(r.size-indexTOCLen-crc32.Size), indexTOCLen+crc32.Size)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"get TOC from object storage of %s\", r.path)\n\t}\n\n\ttocBytes, err := io.ReadAll(rc)\n\tif err != nil {\n\t\trunutil.CloseWithErrCapture(&err, rc, \"close toc reader\")\n\t\treturn nil, errors.Wrapf(err, \"get TOC from object storage of %s\", r.path)\n\t}\n\n\tif err := rc.Close(); err != nil {\n\t\treturn nil, errors.Wrap(err, \"close toc reader\")\n\t}\n\n\ttoc, err := index.NewTOCFromByteSlice(realByteSlice(tocBytes))\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"new TOC\")\n\t}\n\treturn toc, nil\n}\n\nfunc (r *chunkedIndexReader) CopySymbols(w io.Writer, buf []byte) (err error) {\n\trc, err := r.bkt.GetRange(r.ctx, r.path, int64(r.toc.Symbols), int64(r.toc.Series-r.toc.Symbols))\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"get symbols from object storage of %s\", r.path)\n\t}\n\tdefer runutil.CloseWithErrCapture(&err, rc, \"close symbol reader\")\n\n\tif _, err := io.CopyBuffer(w, rc, buf); err != nil {\n\t\treturn errors.Wrap(err, \"copy symbols\")","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/block/indexheader/binary_reader.go#L224-L260","documentation":"After successfully reading the TOC bytes, readTOC closes the range-read body. If rc.Close() returns an error (some backends validate checksums or finalize uploads on Close), the read is abandoned with this 'close toc reader' error rather than returning a possibly unverifiable TOC.","triggerScenarios":"newChunkedIndexReader -> readTOC where the HTTP/body Close call after a successful ReadAll fails — e.g. S3 client-side CRC/validation error at body finalization, or custom objstore bucket implementations returning errors from Close.","commonSituations":"Custom objstore wrappers that do work in Close and fail; corrupted transfer detected at body close by SDK checksum validation; resource cleanup failures in exotic backends.","solutions":["Look at the wrapped Close error: if it is checksum validation, treat the read as failed and retry the fetch.","If a custom bucket implementation is in use, ensure Close only performs cleanup or surfaces genuine transfer errors.","Retry the whole readTOC; transient body-close errors resolve on re-fetch.","Check objstore client versions for known SDK close/validation bugs and upgrade."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := readTOC(ctx)\nvar closeErr interface{ Unwrap() error }\nif errors.As(err, &closeErr) && isChecksumError(errors.Unwrap(err)) {\n    return retry(readTOC)\n}","preventionTips":["Keep objstore SDKs up to date to avoid close-time validation bugs.","If using custom bucket wrappers, ensure Close only reports genuine transfer errors.","Treat any close-stage checksum error as a full-read failure and refetch."],"tags":["object-storage","resource-cleanup","io","thanos"],"backgroundTag":"file-read-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"}