thanos-io/thanos · warning

close

Error message

close

What it means

When WriteSeries finishes (or panics via defer), it closes all opened block readers with tsdb_errors.CloseAll. If any Close returns an error, it is wrapped with "close" and merged into the returned multi-error. This is a cleanup-path failure: the compaction itself may have succeeded but resource release failed.

Solutions

  1. Inspect the wrapped inner error to find which reader's Close failed and fix the underlying I/O or double-close cause
  2. Check disk health and file descriptor limits (ulimit) on the node running compaction
  3. If you implement block.Reader/Index/Chunks, make Close idempotent and nil-safe
  4. Retry the compaction after transient I/O errors
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check block files are readable before compaction
for _, b := range readers {
    if _, err := b.Index(); err != nil { return err }
    if _, err := b.Chunks(); err != nil { return err }
}

Try / catch

if err := comp.WriteSeries(ctx, readers, sWriter, progress); err != nil {
    if strings.Contains(err.Error(), "close") {
        // cleanup-path failure; log and inspect underlying cause
        logger.Warn("compaction ok but reader close failed", "err", err)
    }
    return err
}

Prevention

When it happens

Trigger: b.Index() or b.Chunks() returned a reader whose Close fails — e.g. underlying file descriptor already closed, disk I/O error on close, or a custom block.Reader implementation returning an error from Close.

Common situations: Disk errors/EIO on unclean storage, double-close bugs in custom block reader implementations, or file-descriptor exhaustion causing close failures on packed block files.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/c71846b0f5f27b07. Report an issue: GitHub.

Appendix: source

Thrown at pkg/compactv2/compactor.go:89

	s := New(tmpDir, logger, changeLogger, pool)
	s.dryRun = true
	return s
}

// TODO(bwplotka): Upstream this.
func (w *Compactor) WriteSeries(ctx context.Context, readers []block.Reader, sWriter block.Writer, p ProgressLogger, modifiers ...Modifier) (err error) {
	if len(readers) == 0 {
		return errors.New("cannot write from no readers")
	}

	var (
		sReaders []seriesReader
		closers  []io.Closer
	)
	defer func() {
		errs := tsdb_errors.NewMulti(err)
		if cerr := tsdb_errors.CloseAll(closers); cerr != nil {
			errs.Add(errors.Wrap(cerr, "close"))
		}
		err = errs.Err()
	}()

	for _, b := range readers {
		indexr, err := b.Index()
		if err != nil {
			return errors.Wrapf(err, "open index reader for block %+v", b.Meta())
		}
		closers = append(closers, indexr)

		chunkr, err := b.Chunks()
		if err != nil {
			return errors.Wrapf(err, "open chunk reader for block %+v", b.Meta())
		}
		closers = append(closers, chunkr)
		sReaders = append(sReaders, seriesReader{ir: indexr, cr: chunkr})
	}

View on GitHub (pinned to 35b8b99117)