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.

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)

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.