thanos-io/thanos · error

scanning file

Error message

scanning file %s

What it means

After scanning all frames, NewLocalStoreFromJSONMmappableFile checks skanner.Err() and wraps any scanner-level I/O failure with "scanning file <path>". This is a low-level read error on the backing file, not a JSON schema issue.

Solutions

  1. Check file permissions and that the path exists and is readable by the process.
  2. Verify disk health / mount stability for the backing storage.
  3. Regenerate the local store file if it was truncated or corrupted.
  4. Inspect the wrapped inner error for the exact OS-level cause.

Example fix

// before
store, err := NewLocalStoreFromJSONMmappableFile(mmappablePath)
// after
if _, err := os.Stat(mmappablePath); err != nil {
  // regenerate dump first
}
store, err := NewLocalStoreFromJSONMmappableFile(mmappablePath)
Defensive patterns

Strategy: try-catch

Validate before calling

f, err := os.Open(path)
if err != nil { return err }
if fi, err := f.Stat(); err != nil || fi.Size() == 0 { return errors.New("missing or empty file") }

Try / catch

if err != nil {
  if os.IsPermission(errors.Unwrap(err)) { /* fix perms */ }
  // else regenerate the file and retry load
}

Prevention

When it happens

Trigger: The mappable file is unreadable mid-scan: disk I/O error, permission change after open, or the file being truncated/deleted while scanning.

Common situations: Files on flaky network mounts (NFS/EFS); permission issues after container restarts; files deleted by cleanup jobs while being loaded.

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/0f19c39853f656aa. Report an issue: GitHub.

Appendix: source

Thrown at pkg/store/local.go:107

		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)
	}

	if err := skanner.Err(); err != nil {
		return nil, errors.Wrapf(err, "scanning file %s", path)
	}
	level.Info(logger).Log("msg", "loading JSON file succeeded", "file", path, "series", len(s.series))
	return s, nil
}

// ScanGRPCCurlProtoStreamMessages allows to tokenize each streamed gRPC message from grpcurl tool.
func ScanGRPCCurlProtoStreamMessages(data []byte, atEOF bool) (advance int, token []byte, err error) {
	var delim = []byte(`}
{`)
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	if idx := bytes.LastIndex(data, delim); idx != -1 {
		return idx + 2, data[:idx+1], nil
	}
	// If we're at EOF, let's return all.
	if atEOF {
		return len(data), data, nil

View on GitHub (pinned to 35b8b99117)