{"record":{"id":"7fd6ddab73fbac8a","repo":"thanos-io/thanos","slug":"cannot-write-from-no-readers","errorCode":null,"errorMessage":"cannot write from no readers","messagePattern":"cannot write from no readers","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/compactv2/compactor.go","lineNumber":79,"sourceCode":"func New(tmpDir string, logger log.Logger, changeLogger ChangeLogger, pool chunkenc.Pool) *Compactor {\n\treturn &Compactor{\n\t\ttmpDir:       tmpDir,\n\t\tlogger:       logger,\n\t\tchangeLogger: changeLogger,\n\t\tchunkPool:    pool,\n\t}\n}\n\nfunc NewDryRun(tmpDir string, logger log.Logger, changeLogger ChangeLogger, pool chunkenc.Pool) *Compactor {\n\ts := New(tmpDir, logger, changeLogger, pool)\n\ts.dryRun = true\n\treturn s\n}\n\n// TODO(bwplotka): Upstream this.\nfunc (w *Compactor) WriteSeries(ctx context.Context, readers []block.Reader, sWriter block.Writer, p ProgressLogger, modifiers ...Modifier) (err error) {\n\tif len(readers) == 0 {\n\t\treturn errors.New(\"cannot write from no readers\")\n\t}\n\n\tvar (\n\t\tsReaders []seriesReader\n\t\tclosers  []io.Closer\n\t)\n\tdefer func() {\n\t\terrs := tsdb_errors.NewMulti(err)\n\t\tif cerr := tsdb_errors.CloseAll(closers); cerr != nil {\n\t\t\terrs.Add(errors.Wrap(cerr, \"close\"))\n\t\t}\n\t\terr = errs.Err()\n\t}()\n\n\tfor _, b := range readers {\n\t\tindexr, err := b.Index()\n\t\tif err != nil {\n\t\t\treturn errors.Wrapf(err, \"open index reader for block %+v\", b.Meta())","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/compactv2/compactor.go#L61-L97","documentation":"Compactor.WriteSeries refuses to run when the readers slice is empty. Compaction works by merging series from one or more block readers into a new block; with zero readers there is nothing to compact, so the call fails fast with a sentinel error instead of producing an empty or corrupt block.","triggerScenarios":"Calling WriteSeries with a nil or zero-length []block.Reader slice, e.g. after filtering out all source blocks or passing a slice built from an empty compaction group.","commonSituations":"Compaction planning code selecting source blocks yields no candidates (e.g. all blocks excluded by time range or already compacted) but WriteSeries is still invoked; tests or tooling that construct Compactor calls manually with no inputs.","solutions":["Ensure at least one block.Reader is passed; check len(readers) before calling WriteSeries and skip/return early if empty","Fix the block-selection/compaction-plan logic so a plan with zero sources is never executed","If an empty input is legitimate for your flow, guard the call site: if len(readers) == 0 { return nil }"],"exampleFix":"// before\nif err := comp.WriteSeries(ctx, readers, w, progress); err != nil { ... }\n// after\nif len(readers) > 0 {\n    if err := comp.WriteSeries(ctx, readers, w, progress); err != nil { ... }\n}","handlingStrategy":"validation","validationCode":"if len(readers) == 0 {\n    // nothing to compact; skip instead of calling WriteSeries\n    return nil\n}\nif err := comp.WriteSeries(ctx, readers, sWriter, progress); err != nil {\n    return err\n}","typeGuard":null,"tryCatchPattern":"if err := comp.WriteSeries(ctx, readers, sWriter, progress); err != nil {\n    if err.Error() == \"cannot write from no readers\" {\n        return nil // treat empty input as a no-op\n    }\n    return err\n}","preventionTips":["Always validate len(readers) > 0 before invoking WriteSeries","Make block-selection logic return an explicit 'no plan' state instead of an empty reader list","Add unit tests covering empty compaction groups"],"tags":["go","compaction","validation"],"backgroundTag":"empty-required-field","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"}