golang/go · error

file changed between reads

Error message

file changed between reads

What it means

When embedding a small file (1KB or less), the compiler stats the file to get its size, then reads the entire content with io.ReadAll. After reading, it checks that len(data) equals the stat-reported size. If they differ, the file was modified between the stat and read operations, and this error is returned to prevent embedding inconsistent data.

Solutions

  1. Ensure file generation completes before compilation starts — serialize build steps
  2. Use atomic file writes: write to a temp file, then os.Rename (rename is atomic on most filesystems)
  3. Add a dependency in your build system so generated files are ready before go build
  4. Avoid running file generators and the compiler in parallel on the same files
Defensive patterns

Strategy: validation

Validate before calling

// Ensure embedded files are stable by snapshotting them before build
func snapshotEmbedFiles(dir string) error {
    return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if !info.IsDir() {
            // Read the file twice and compare — if it changes, fail
            data1, err := os.ReadFile(path)
            if err != nil {
                return err
            }
            data2, err := os.ReadFile(path)
            if err != nil {
                return err
            }
            if !bytes.Equal(data1, data2) {
                return fmt.Errorf("file %s is being modified concurrently — stabilize before build", path)
            }
        }
        return nil
    })
}

Prevention

When it happens

Trigger: The embedded file is concurrently modified by another process between the stat() syscall and the read() syscall during compilation. The window is very small (sub-millisecond) so this requires active concurrent writes.

Common situations: Build systems that generate files concurrently with compilation (make -j with overlapping targets). File watchers or linters that rewrite files. Code generators that haven't finished writing when the compiler starts. Network filesystems with eventual consistency.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/327df37ab910f9ae. Report an issue: GitHub.

Appendix: source

Thrown at src/cmd/compile/internal/staticdata/data.go:142

	if err != nil {
		return nil, 0, err
	}
	defer f.Close()
	info, err := f.Stat()
	if err != nil {
		return nil, 0, err
	}
	if !info.Mode().IsRegular() {
		return nil, 0, fmt.Errorf("not a regular file")
	}
	size := info.Size()
	if size <= 1*1024 {
		data, err := io.ReadAll(f)
		if err != nil {
			return nil, 0, err
		}
		if int64(len(data)) != size {
			return nil, 0, fmt.Errorf("file changed between reads")
		}
		var sym *obj.LSym
		if readonly {
			sym = StringSym(pos, string(data))
		} else {
			sym = slicedata(pos, string(data))
		}
		if len(hashBytes) > 0 {
			sum := hash.Sum32(data)
			copy(hashBytes, sum[:])
		}
		return sym, size, nil
	}
	if size > maxFileSize {
		// ggloblsym takes an int32,
		// and probably the rest of the toolchain
		// can't handle such big symbols either.
		// See golang.org/issue/9862.

View on GitHub (pinned to b6b368adc5)