thanos-io/thanos · error
label set contains duplicate label names
Error message
label set contains duplicate label names
What it means
ErrDuplicateLabels is a sentinel error signaling that a label set contains two labels with the same name. The capnproto writer detects it when validating labels: consecutive sorted label names compare equal (cmp == 0), so the write is rejected because duplicate names make the series ambiguous.
Solutions
- Deduplicate labels by name before writing (merge external labels with labels.Labels.Range/labels.New).
- On the sending side, use relabeling to drop duplicate labels rather than appending raw.
- In custom writers, use labels.Builder which rejects/overwrites duplicates.
Example fix
// before
lbls := labels.FromStrings("env", "prod", "env", "dev") // duplicate
// after
b := labels.NewBuilder(labels.EmptyLabels())
b.Set("env", "prod") // last-wins, no duplicates Defensive patterns
Strategy: validation
Validate before calling
func hasDuplicateLabels(lbls labels.Labels) error {
var prev string
dup := false
lbls.Range(func(l labels.Label) error {
if l.Name == prev { dup = true }
prev = l.Name
return nil
})
if dup { return errors.New("duplicate label names") }
return nil
} Type guard
func isDuplicateLabelsErr(err error) bool { return errors.Is(err, labelpb.ErrDuplicateLabels) } Try / catch
if err := writer.Write(ctx, series); err != nil {
if errors.Is(err, labelpb.ErrDuplicateLabels) {
series.Labels = dedupe(series.Labels) // last-wins merge
return writer.Write(ctx, series)
}
return err
} Prevention
- Merge external labels via labels.Builder instead of appending raw.
- Dedupe label maps before constructing labels.Labels.
- When proxying writes between tenants, check for injected-label collisions.
When it happens
Trigger: Writing a TimeSeries whose labels list contains the same name twice (e.g. two 'env' labels) via pkg/receive/capnproto_writer.go, or when custom client code appends external labels without first merging with existing ones.
Common situations: Client libraries that append external/target labels without deduplicating; multi-tenant proxies injecting labels already present in the payload; hand-constructed labels.Labels from unsorted, duplicated name/value pairs.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- add series
- out of order labels
- label set contains a label with empty name or value
- unsupported format for label
- invalid label name
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/9b7f73f947fa6e91.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/store/labelpb/label.go:28
"encoding/json"
"fmt"
"io"
"sort"
"strings"
"sync"
"unique"
"unsafe"
"github.com/VictoriaMetrics/easyproto"
"github.com/cespare/xxhash/v2"
"github.com/pkg/errors"
"github.com/prometheus/prometheus/model/labels"
)
var (
ErrOutOfOrderLabels = errors.New("out of order labels")
ErrEmptyLabels = errors.New("label set contains a label with empty name or value")
ErrDuplicateLabels = errors.New("label set contains duplicate label names")
sep = []byte{'\xff'}
)
func noAllocString(buf []byte) string {
return *(*string)(unsafe.Pointer(&buf))
}
func noAllocBytes(buf string) []byte {
return *(*[]byte)(unsafe.Pointer(&buf))
}
// ZLabelsFromPromLabels converts Prometheus labels to slice of labelpb.ZLabel in type unsafe manner.
// It reuses the same memory. Caller should abort using passed labels.Labels.
func ZLabelsFromPromLabels(lset labels.Labels) []ZLabel {
return *(*[]ZLabel)(unsafe.Pointer(&lset))
}
View on GitHub (pinned to 35b8b99117)