thanos-io/thanos · error

unmarshal storepb.SeriesResponse frame for file

Error message

unmarshal storepb.SeriesResponse frame for file %s

What it means

NewLocalStoreFromJSONMmappableFile reads a file of newline-delimited storepb.SeriesResponse frames (grpcurl output) and unmarshals each frame with jsonpb; any decode failure is wrapped with the file path. It means one of the JSON frames does not match the storepb.SeriesResponse schema.

Solutions

  1. Regenerate the dump with the same Thanos proto version used to read it.
  2. Validate the offending line is valid JSON matching storepb.SeriesResponse (jq, protojson).
  3. Remove or fix the corrupted frame in the file.
  4. Ensure the file is a complete, non-truncated dump.

Example fix

// before
grpcurl -plaintext store:10901 thanos.StoreInfo > dump.json  # mixed responses
// after
grpcurl -plaintext -emit-defaults store:10901 thanos.Store/Series > series_frames.ndjson
Defensive patterns

Strategy: validation

Validate before calling

// validate each line before loading
for i, line := range lines {
  var probe map[string]json.RawMessage
  if json.Unmarshal([]byte(line), &probe) != nil || probe["series"] == nil {
    return fmt.Errorf("frame %d not a SeriesResponse", i)
  }
}

Try / catch

if err != nil {
  var perr *json.UnmarshalTypeError
  if errors.As(err, &perr) { /* report offending line/field */ }
}

Prevention

When it happens

Trigger: Loading a cached/dumped store response file where a frame was truncated, written by an incompatible proto version, or is not a SeriesResponse JSON at all.

Common situations: Manually captured grpcurl output edited/corrupted; version mismatch between the tool that dumped the file and Thanos proto definitions; partially written file from an interrupted dump.

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


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

Appendix: source

Thrown at pkg/store/local.go:86

		c:         f,
	}

	// Do quick pass for in-mem index.
	content := f.Bytes()
	contentStart := bytes.Index(content, []byte("{"))
	if contentStart != -1 {
		content = content[contentStart:]
	}

	if idx := bytes.LastIndex(content, []byte("}")); idx != -1 {
		content = content[:idx+1]
	}

	skanner := NewNoCopyScanner(content, split)
	resp := &storepb.SeriesResponse{}
	for skanner.Scan() {
		if err := jsonpb.Unmarshal(bytes.NewReader(skanner.Bytes()), resp); err != nil {
			return nil, errors.Wrapf(err, "unmarshal storepb.SeriesResponse frame for file %s", path)
		}
		series := resp.GetSeries()
		if series == nil {
			level.Warn(logger).Log("msg", "not a valid series", "frame", resp.String())
			continue
		}
		chks := make([]int, 0, len(series.Chunks))
		// Sort chunks in separate slice by MinTime for easier lookup. Find global max and min.
		for ci := range series.Chunks {
			chks = append(chks, ci)
		}

		sort.Slice(chks, func(i, j int) bool {
			return series.Chunks[chks[i]].MinTime < series.Chunks[chks[j]].MinTime
		})
		s.series = append(s.series, series)
		s.sortedChunks = append(s.sortedChunks, chks)
	}

View on GitHub (pinned to 35b8b99117)