{"id":"cdd888c605a1807c","repo":"gohugoio/hugo","slug":"failed-to-acquire-a-build-lock-w","errorCode":null,"errorMessage":"failed to acquire a build lock: %w","messagePattern":"failed to acquire a build lock: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hugolib/hugo_sites_build.go","lineNumber":97,"sourceCode":"\t}\n\n\tinfol := h.Log.InfoCommand(\"build\")\n\tdefer loggers.TimeTrackf(infol, time.Now(), nil, \"\")\n\tdefer func() {\n\t\th.reportProgress(func() (state terminal.ProgressState, progress float64) {\n\t\t\treturn terminal.ProgressHidden, 1.0\n\t\t})\n\t\th.BuildState.BuildCounter.Add(1)\n\t}()\n\n\tif h.Deps == nil {\n\t\tpanic(\"must have deps\")\n\t}\n\n\tif !config.NoBuildLock {\n\t\tunlock, err := h.BaseFs.LockBuild()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to acquire a build lock: %w\", err)\n\t\t}\n\t\tdefer unlock()\n\t}\n\n\tdefer func() {\n\t\tfor _, s := range h.Sites {\n\t\t\ts.Deps.BuildEndListeners.Notify()\n\t\t}\n\t}()\n\n\terrCollector := h.StartErrorCollector()\n\terrs := make(chan error)\n\n\tgo func(from, to chan error) {\n\t\tvar errors []error\n\t\ti := 0\n\t\tfor e := range from {\n\t\t\ti++","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/hugolib/hugo_sites_build.go#L79-L115","documentation":"Before building, Hugo takes a file-based lock (via BaseFs.LockBuild) to prevent two Hugo processes from writing the same destination concurrently. This error means acquiring that lock file (.hugo_build.lock in the project root) failed — either another process holds it past the timeout or the lock file can't be created.","triggerScenarios":"HugoSites.Build with config.NoBuildLock=false when another hugo build/server against the same source is running and holds .hugo_build.lock, when a crashed process left a stale lock, or when the project directory is read-only so the lock file cannot be created.","commonSituations":"Running `hugo` while `hugo server` is still up in another terminal; CI jobs building the same checkout in parallel; read-only mounted source directories in containers; stale .hugo_build.lock after an OOM-killed build.","solutions":["Stop the other Hugo process using the same project (commonly a running `hugo server`).","Delete a stale `.hugo_build.lock` in the project root if no other process is running.","Ensure the project directory is writable by the build user.","If concurrent builds are intentional and write to different destinations, pass `--noBuildLock`."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// ensure only one build runs per HugoSites instance\n// serialize callers: guard Build() with your own queue if invoking concurrently","typeGuard":null,"tryCatchPattern":"err := h.Build(cfg)\nif err != nil && strings.Contains(err.Error(), \"failed to acquire a build lock\") {\n    // another build in flight; wait and retry once\n    time.Sleep(500 * time.Millisecond)\n    err = h.Build(cfg)\n}","preventionTips":["Never call Build concurrently on the same HugoSites instance; serialize through a single goroutine or channel","In server/watch mode let Hugo drive rebuilds instead of triggering them externally","If embedding Hugo, wrap Build in your own mutex so lock contention surfaces in your code, not deep in the build"],"tags":["hugo","build","lock","filesystem","concurrency"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}