thanos-io/thanos · error
empty external labels are not allowed for Thanos block.
Error message
empty external labels are not allowed for Thanos block.
What it means
When checkExternalLabels is true, upload() refuses to upload a block whose meta.json lacks Thanos external labels. Thanos requires external labels to build unique block IDs across replicas and to route queries; a block without them would be ambiguous in the bucket.
Solutions
- Configure external_labels in Prometheus config so meta.json carries Thanos labels
- Pass checkExternalLabels=false only if you intentionally accept label-less blocks
- Regenerate meta.json with correct thanos.labels via metadata.Meta before upload
Example fix
// before
meta.Thanos.Labels = map[string]string{}
// after
meta.Thanos.Labels = map[string]string{"prometheus": "cluster-a", "replica": "0"} Defensive patterns
Strategy: validation
Validate before calling
meta, err := metadata.ReadFromDir(bdir)
if err != nil { return err }
if len(meta.Thanos.Labels) == 0 { return errors.New("block has no Thanos external labels") } Prevention
- Configure external_labels in Prometheus before shipping blocks
- Keep sidecar/shipper versions consistent with Prometheus config
- Sanity-check meta.json contents in CI for custom upload tooling
When it happens
Trigger: Uploading a Prometheus block whose meta.json has an empty meta.Thanos.Labels map while checkExternalLabels=true (the default via block.Upload).
Common situations: Prometheus not configured with external_labels; meta.json written by older/tooling versions that skipped Thanos labels; hand-crafted meta.json files; sidecar/shipper misconfiguration.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- parse limit configuration
- --receive.lazy-retrieval-max-buffered-responses must be > 0
- found that Prometheus and Thanos use different paths for…
- found that TSDB Max time is
- filter metas
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/c31572d51b61e1a8.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/block/block.go:134
if !df.IsDir() {
return errors.Errorf("%s is not a directory", bdir)
}
// Verify dir.
id, err := ulid.Parse(df.Name())
if err != nil {
return errors.Wrap(err, "not a block dir")
}
meta, err := metadata.ReadFromDir(bdir)
if err != nil {
// No meta or broken meta file.
return errors.Wrap(err, "read meta")
}
if checkExternalLabels {
if len(meta.Thanos.Labels) == 0 {
return errors.New("empty external labels are not allowed for Thanos block.")
}
}
metaEncoded := strings.Builder{}
meta.Thanos.Files, err = GatherFileStats(bdir, hf, logger)
if err != nil {
return errors.Wrap(err, "gather meta file stats")
}
if err := objstore.UploadDir(ctx, logger, bkt, filepath.Join(bdir, ChunksDirname), path.Join(id.String(), ChunksDirname), options...); err != nil {
return errors.Wrap(err, "upload chunks")
}
if err := objstore.UploadFile(ctx, logger, bkt, filepath.Join(bdir, IndexFilename), path.Join(id.String(), IndexFilename)); err != nil {
return errors.Wrap(err, "upload index")
}
meta.Thanos.UploadTime = time.Now().UTC()View on GitHub (pinned to 35b8b99117)