thanos-io/thanos · error

read parquet metadata files

Error message

read parquet metadata files

What it means

Returned when one or more of the concurrently executed parquet metadata file reads failed (merrs aggregated). It wraps merrs.Err() with 'read parquet metadata files', distinct from the bucket iteration error above it.

Solutions

  1. Check the inner aggregated errors for the specific file paths and root causes
  2. Delete or repair corrupt/truncated parquet metadata files, or re-run the parquet converter
  3. Verify read permissions on the parquet metadata prefix
  4. Retry if the underlying errors are transient (throttling, network)
Defensive patterns

Strategy: try-catch

Try / catch

if err := refreshMigratedBlocks(ctx); err != nil {
    var errs []error
    if errors.As(err, &errs) {
        for _, e := range errs { log.Printf("parquet meta read: %v", e) }
    }
    // keep previous migrated-blocks set rather than wiping it
}

Prevention

When it happens

Trigger: Any readMigratedBlocksFromParquetMetadata failure for a listed parquet metadata file — Get errors, io.ReadAll errors, easyproto field parse failures — aggregated by the errgroup workers.

Common situations: Objects deleted between listing and reading; truncated parquet metadata from an interrupted parquet conversion; permission or throttling errors on Get.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at pkg/block/fetcher.go:1142

		return f.bkt.Iter(ctx, "", func(name string) error {
			if path.Base(name) == parquetMetaFileName {
				select {
				case ch <- name:
				case <-ctx.Done():
					return ctx.Err()
				}
			}
			return nil
		}, objstore.WithRecursiveIter())
	})

	if err := eg.Wait(); err != nil {
		return errors.Wrap(err, "iterate bucket for parquet metadata")
	}

	if len(merrs) > 0 {
		return errors.Wrap(merrs.Err(), "read parquet metadata files")
	}

	// NOTE(GiedriusS): we need to be extra careful here because Parquet converter trims chunks exactly to the day's range.
	// The input to the Parquet converter might not be aligned exactly so we NEED to have some overlap.
	// So, only delete metas if the migrated blocks cover each day *fully*.
	for id := range filterMigratedBlocksByDayCoverage(allMigratedBlocks, metas) {
		delete(metas, id)
		synced.WithLabelValues("parquet-converted").Inc()
	}

	return nil
}

func filterMigratedBlocksByDayCoverage(migratedBlocks map[ulid.ULID]struct{}, metas map[ulid.ULID]*metadata.Meta) map[ulid.ULID]struct{} {
	type blockRange struct {
		id               ulid.ULID
		minTime, maxTime int64
	}

View on GitHub (pinned to 35b8b99117)