thanos-io/thanos · error

parse block ID from parquet metadata file

Error message

parse block ID %q from parquet metadata file: %v

What it means

Wraps ulid.Parse on a string field (field 6, convertedFromBLIDs) inside the parquet conversion metadata protobuf: the recorded block ID is not a valid ULID, meaning the migration metadata itself is corrupt. The bad string and file path are included.

Solutions

  1. Re-generate the parquet metadata with a correct converter
  2. Verify ULID formatting in metadata
  3. Skip the offending metadata file
Defensive patterns

Strategy: type-guard

Validate before calling

if _, err := ulid.Parse(storedID); err != nil {
    return fmt.Errorf("metadata %s holds invalid block id %q", path, storedID)
}

Try / catch

id, err := ulid.Parse(u)
if err != nil {
    log.Printf("skipping %s: bad ULID %q", path, u)
    continue // or delete/regenerate the file
}

Prevention

When it happens

Trigger: ulid.Parse(u) fails inside the field-6 handler — the stored string is not a valid 26-character canonical ULID (empty, truncated, wrong encoding, or binary garbage).

Common situations: Corrupt or partially written parquet metadata; a writer that stored non-canonical ULIDs (lowercase or non-standard encoding) or a different ID format due to version skew.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at pkg/block/fetcher.go:1254

	if err != nil {
		return nil, errors.Wrapf(err, "read parquet metadata file: %v", path)
	}

	var fc easyproto.FieldContext
	for len(content) > 0 {
		content, err = fc.NextField(content)
		if err != nil {
			return nil, errors.Wrapf(err, "read next field from parquet metadata file: %v", path)
		}
		switch fc.FieldNum {
		case 6:
			u, ok := fc.String()
			if !ok {
				return nil, errors.Wrapf(err, "read convertedFromBLIDs field from parquet metadata file: %v", path)
			}
			id, err := ulid.Parse(u)
			if err != nil {
				return nil, errors.Wrapf(err, "parse block ID %q from parquet metadata file: %v", u, path)
			}
			migratedBlocks[id] = struct{}{}
		}
	}

	return migratedBlocks, nil
}

// IgnoreDeletionMarkFilter is a filter that filters out the blocks that are marked for deletion after a given delay.
// The delay duration is to make sure that the replacement block can be fetched before we filter out the old block.
// Delay is not considered when computing DeletionMarkBlocks map.
// Not go-routine safe.
type IgnoreDeletionMarkFilter struct {
	logger      log.Logger
	delay       time.Duration
	concurrency int
	bkt         objstore.InstrumentedBucketReader

View on GitHub (pinned to 35b8b99117)