{"record":{"id":"f12663f45ed942b1","repo":"golang/go","slug":"existing-contents-have-changed-since-last-read","errorCode":null,"errorMessage":"existing contents have changed since last read","messagePattern":"existing contents have changed since last read","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/modload/init.go","lineNumber":2092,"sourceCode":"\tif unlock, err := modfetch.SideLock(ctx); err == nil {\n\t\tdefer unlock()\n\t}\n\n\terr = lockedfile.Transform(modFilePath, func(old []byte) ([]byte, error) {\n\t\tif bytes.Equal(old, updatedGoMod) {\n\t\t\t// The go.mod file is already equal to new, possibly as the result of some\n\t\t\t// other process.\n\t\t\treturn nil, errNoChange\n\t\t}\n\n\t\tif index != nil && !bytes.Equal(old, index.data) {\n\t\t\t// The contents of the go.mod file have changed. In theory we could add all\n\t\t\t// of the new modules to the build list, recompute, and check whether any\n\t\t\t// module in *our* build list got bumped to a different version, but that's\n\t\t\t// a lot of work for marginal benefit. Instead, fail the command: if users\n\t\t\t// want to run concurrent commands, they need to start with a complete,\n\t\t\t// consistent module definition.\n\t\t\treturn nil, fmt.Errorf(\"existing contents have changed since last read\")\n\t\t}\n\n\t\treturn updatedGoMod, nil\n\t})\n\n\tif err != nil && err != errNoChange {\n\t\treturn fmt.Errorf(\"updating go.mod: %w\", err)\n\t}\n\treturn nil\n}\n\n// keepSums returns the set of modules (and go.mod file entries) for which\n// checksums would be needed in order to reload the same set of packages\n// loaded by the most recent call to LoadPackages or ImportFromFiles,\n// including any go.mod files needed to reconstruct the MVS result\n// or identify go versions,\n// in addition to the checksums for every module in keepMods.\nfunc keepSums(ld *Loader, ctx context.Context, pld *packageLoader, rs *Requirements, which whichSums) map[module.Version]bool {","sourceCodeStart":2074,"sourceCodeEnd":2110,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/modload/init.go#L2074-L2110","documentation":"During go.mod update, modload held a cached copy (index.data) of the file but on the write path the on-disk bytes ('old') differ from the cached snapshot, meaning a concurrent process edited go.mod between read and write. Rather than reconcile, the command fails fast to preserve consistency.","triggerScenarios":"Two 'go' commands (or an editor / hook) modified go.mod concurrently; the current command read go.mod, planned an update, and on re-read for write found different bytes than index.data.","commonSituations":"Running 'go mod tidy' and 'go build' in parallel; an IDE auto-running goimports/go mod while a terminal command runs; a file watcher / pre-commit hook rewriting go.mod mid-operation.","solutions":["Serialize go commands: run one to completion before starting another in the same module.","Disable editor auto-format/save hooks that touch go.mod during builds.","Re-run the command after the concurrent one finishes (the fresh read picks up the new contents).","Use a workspace (go.work) or separate checkouts to allow parallelism without contention."],"exampleFix":"// before\n$ go mod tidy &  go build ./...   // concurrent go.mod writers\n// updating go.mod: existing contents have changed since last read\n\n// after\n$ go mod tidy && go build ./...     // sequential","handlingStrategy":"validation","validationCode":"// Detect concurrent go.mod writers before running builds (best-effort file lock).\nf, err := os.OpenFile(\"go.mod\", os.O_RDWR, 0644)\nif err != nil { return err }\ndefer f.Close()\nif err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {\n    return errors.New(\"go.mod is locked by another go command\")\n}\n// proceed with the build","typeGuard":null,"tryCatchPattern":"out, err := exec.Command(\"go\", \"mod\", \"tidy\").CombinedOutput()\nif err != nil && bytes.Contains(out, []byte(\"existing contents have changed since last read\")) {\n    // wait for the other writer to finish, then retry ONCE with fresh state\n    time.Sleep(2 * time.Second) // only because we cannot observe the lock directly here\n    out, err = exec.Command(\"go\", \"mod\", \"tidy\").CombinedOutput()\n}\nreturn err","preventionTips":["Do not run multiple go commands that write go.mod in parallel.","Disable IDE auto-tidy-on-save during terminal builds.","Use 'go work' or separate checkouts for parallel workflows.","Pre-commit hooks should run sequentially, not concurrently with builds."],"tags":["go-mod","concurrency","write-conflict","consistency"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}