thanos-io/thanos · error
filter metas
Error message
filter metas
What it means
In fetchMetadata, after loading metadata the supplied MetadataFilters are applied via filter.Filter(ctx, metas, ...). If any filter returns an error, the whole metadata fetch aborts and the error is wrapped with "filter metas". It signals a user-supplied filter (e.g. consistency delay, time-partition, or custom label filters) failed, not the object store itself.
Solutions
- Inspect the wrapped error to identify which filter failed and why.
- Fix the misconfiguration of that filter (labels, time partitions, consistency delay settings).
- If a custom MetadataFilter is at fault, make it tolerant: log-and-skip blocks instead of returning an error.
- Temporarily remove the failing filter from the sync options to confirm it is the cause, then correct it.
Example fix
// before: custom filter that aborts sync on any block
func (f *MyFilter) Filter(ctx context.Context, m map[ulid.ULID]*metadata.Meta, ...) error {
return fmt.Errorf("bad block %s", ulid)
}
// after: log and skip offending blocks instead
for id := range bad { delete(m, id); level.Warn(logger).Log("msg", "filter dropped block", "block", id) }
return nil Defensive patterns
Strategy: try-catch
Validate before calling
// Unit-test custom filters with representative metadata before deploying
if err := myFilter.Filter(ctx, testMetas, metrics.NewBlocksSynced(metrics, nil, []string{}), nil); err != nil {
// filter is not tolerant; fix before enabling in sync
} Try / catch
metas, err := fetcher.FetchMetadata(ctx, filtered)
if err != nil && strings.Contains(err.Error(), "filter metas") {
// identify the failing filter from the wrapped error; disable/correct it, then retry
} Prevention
- Make custom MetadataFilter implementations log-and-skip instead of returning errors.
- Keep consistency delay and label-selectors filter configuration valid for the blocks present.
- Test new filters in staging buckets before production sync.
- Keep wrapped-error context in filters so the culprit is identifiable from logs.
When it happens
Trigger: Calling fetchMetadata with filters configured (e.g. ConsistencyDelayMetaFilter, TimePartitionMetaFilter, LabelSelectorMetaFilter, SidecarMetaFilter) where the filter's Filter method returns an error — typically a wrapped underlying failure inside the filter.
Common situations: Custom MetadataFilter implementations returning errors on unexpected metadata; consistency-delay filter clock/time parsing issues; sidecar filter failing a store-API call while filtering; misconfigured label selectors in custom filters.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- empty external labels are not allowed for Thanos block.
- raw resolution must be higher than the minimum block size…
- 5m resolution retention must be higher than the minimum…
- sync before first pass of downsampling
- sync before second pass of downsampling
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/2c90531b91d14739.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/block/fetcher.go:687
return f.fetchMetadata(ctx)
})
if err != nil {
return nil, nil, err
}
resp := v.(response)
// Copy as same response might be reused by different goroutines.
metas := make(map[ulid.ULID]*metadata.Meta, len(resp.metas))
maps.Copy(metas, resp.metas)
metrics.Synced.WithLabelValues(FailedMeta).Set(float64(len(resp.metaErrs)))
metrics.Synced.WithLabelValues(NoMeta).Set(resp.noMetas)
metrics.Synced.WithLabelValues(CorruptedMeta).Set(resp.corruptedMetas)
for _, filter := range filters {
// NOTE: filter can update synced metric accordingly to the reason of the exclude.
if err := filter.Filter(ctx, metas, metrics.Synced, metrics.Modified); err != nil {
return nil, nil, errors.Wrap(err, "filter metas")
}
}
metrics.Synced.WithLabelValues(LoadedMeta).Set(float64(len(metas)))
if len(resp.metaErrs) > 0 {
return metas, resp.partial, errors.Wrap(resp.metaErrs.Err(), "incomplete view")
}
level.Info(f.logger).Log("msg", "successfully synchronized block metadata", "duration", time.Since(start).String(), "duration_ms", time.Since(start).Milliseconds(), "cached", f.countCached(), "returned", len(metas), "partial", len(resp.partial))
return metas, resp.partial, nil
}
func (f *BaseFetcher) countCached() int {
f.mtx.Lock()
defer f.mtx.Unlock()
var i int
f.cached.Range(func(_, _ any) bool {View on GitHub (pinned to 35b8b99117)