golang/go · error
error: size of %s changed during reading (from %d to >=%d by
Error message
error: size of %s changed during reading (from %d to >=%d bytes)
What it means
Thrown by gofmt's reader when io.ReadFull fills the entire size+1 buffer (n>size), proving the file grew beyond the size previously returned by os.Stat. Because src is allocated as size+1, n>size means n==len(src)==size+1, so the true new length is unknown (hence the `>=` in the message). gofmt refuses to format a moving target.
Source
Thrown at src/cmd/gofmt/gofmt.go:361
// read more than size bytes, then the file was modified concurrently.
// (If that happens, we could, say, append to src to finish the read, or
// proceed with a truncated buffer — but the fact that it changed at all
// indicates a possible race with someone editing the file, so we prefer to
// stop to avoid corrupting it.)
src := make([]byte, size+1)
n, err := io.ReadFull(in, src)
switch err {
case nil, io.EOF, io.ErrUnexpectedEOF:
// io.ReadFull returns io.EOF (for an empty file) or io.ErrUnexpectedEOF
// (for a non-empty file) if the file was changed unexpectedly. Continue
// with comparing file sizes in those cases.
default:
return nil, err
}
if n < size {
return nil, fmt.Errorf("error: size of %s changed during reading (from %d to %d bytes)", filename, size, n)
} else if n > size {
return nil, fmt.Errorf("error: size of %s changed during reading (from %d to >=%d bytes)", filename, size, len(src))
}
return src[:n], nil
}
func main() {
// Arbitrarily limit in-flight work to 2MiB times the number of threads.
//
// The actual overhead for the parse tree and output will depend on the
// specifics of the file, but this at least keeps the footprint of the process
// roughly proportional to GOMAXPROCS.
maxWeight := (2 << 20) * int64(runtime.GOMAXPROCS(0))
s := newSequencer(maxWeight, os.Stdout, os.Stderr)
// call gofmtMain in a separate function
// so that it can use defer and have them
// run before the exit.
gofmtMain(s)
os.Exit(s.GetExitCode())View on GitHub (pinned to b6b368adc5)
Solutions
- Re-run gofmt when the file is quiescent.
- Exclude growing files (logs, generated output) from gomt's input set.
- Serialize writers — run `go generate` then gofmt, not in parallel.
- In watch mode, debounce or lock the file before formatting.
Defensive patterns
Strategy: retry
Try / catch
for i := 0; i < 3; i++ {
if err := runGofmt(file); err == nil { break }
if !strings.Contains(err.Error(), "changed during reading") { break }
time.Sleep(100 * time.Millisecond << i)
} Prevention
- Serialize writers — finish `go generate` before gofmt.
- Exclude ever-growing files (logs, generated output) from gofmt input.
- Snapshot/copy the file to a temp path and format the copy if the source is hot.
When it happens
Trigger: A concurrent process appends to or lengthens the file between Stat and ReadFull. The buffer was sized for the old length, ReadFull saturated it and still had bytes available, so the read is unreliable.
Common situations: Live log files being formatted by mistake, code generators appending during gofmt, an editor saving a larger buffer, or two gofmt/process instances racing on the same file.
Related errors
- error: size of %s changed during reading (from %d to %d byte
- inode for file changed since last Lock or RLock
- file changed between reads
- file content changed underfoot
- file larger than size reported by stat
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/3d08554ba52be9bd.
Report an issue: GitHub.