thanos-io/thanos · error

penalty based deduplication needs at least one replica…

Error message

penalty based deduplication needs at least one replica label specified

What it means

Penalty-based deduplication (DedupAlgorithmPenalty) merges series by penalizing replicas, which requires replica labels to distinguish sources. runCompact rejects the combination of --deduplication.penalty with no --deduplication.replica-label flags via this plain errors.New.

Solutions

  1. Add at least one --deduplication.replica-label=<label> when using penalty dedup.
  2. Switch to the default dedup func by removing --deduplication.func=penalty if replica labels aren't available.
  3. Verify the replica label actually exists in your Prometheus external labels.

Example fix

// before
thanos compact --deduplication.func=penalty ...
// after
thanos compact --deduplication.func=penalty --deduplication.replica-label=replica ...
Defensive patterns

Strategy: validation

Validate before calling

if dedupFunc == "penalty" && len(dedupReplicaLabels) == 0 {
    return fmt.Errorf("--deduplication.func=penalty requires at least one --deduplication.replica-label")
}

Prevention

When it happens

Trigger: Starting compact with --deduplication.func=penalty while --deduplication.replica-label is unset or empty.

Common situations: Operators enabling penalty dedup for multi-replica setups but forgetting to declare which labels distinguish replicas (e.g. replica, prometheus).

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at cmd/thanos/compact.go:334

		level.Warn(logger).Log("msg", "Max compaction level is lower than should be", "current", conf.maxCompactionLevel, "default", compactions.maxLevel())
	}

	ctx, cancel := context.WithCancel(context.Background())
	ctx = tracing.ContextWithTracer(ctx, tracer)

	defer func() {
		if rerr != nil {
			cancel()
		}
	}()

	var mergeFunc storage.VerticalChunkSeriesMergeFunc
	switch conf.dedupFunc {
	case compact.DedupAlgorithmPenalty:
		mergeFunc = dedup.NewChunkSeriesMerger()

		if len(dedupReplicaLabels) == 0 {
			return errors.New("penalty based deduplication needs at least one replica label specified")
		}
	case "":
		mergeFunc = storage.NewCompactingChunkSeriesMerger(storage.ChainedSeriesMerge)

	default:
		return errors.Errorf("unsupported deduplication func, got %s", conf.dedupFunc)
	}

	// Instantiate the compactor with different time slices. Timestamps in TSDB
	// are in milliseconds.
	comp, err := tsdb.NewLeveledCompactor(ctx, reg, logutil.GoKitLogToSlog(logger), levels, downsample.NewPool(), mergeFunc)
	if err != nil {
		return errors.Wrap(err, "create compactor")
	}

	var (
		compactDir      = path.Join(conf.dataDir, "compact")
		downsamplingDir = path.Join(conf.dataDir, "downsample")

View on GitHub (pinned to 35b8b99117)