gohugoio/hugo · error
failed to acquire a build lock
Error message
failed to acquire a build lock: %s
What it means
create.NewContent wraps its work in a build lock (create/content.go:85 -> BaseFs.LockBuild). The lock is a file lock at <project>/.hugo_build.lock (lockedfile.MutexAt, basefs.go:489). This wraps any failure to acquire that file lock. It is skipped only when Configs.Base.NoBuildLock is set.
Solutions
- Ensure no other Hugo process is running against the same project (ps/pgrep for hugo).
- Verify write permission on the project root and on .hugo_build.lock; chmod/chown if needed.
- Remove a stale .hugo_build.lock file if no Hugo process is actually running.
- If locking is unnecessary for your workflow, set noBuildLock in the base config to disable it.
Example fix
# before: two concurrent processes fight over the lock $ hugo server & $ hugo new posts/x.md # -> failed to acquire a build lock # after: stop the server first (or set noBuildLock) $ hugo new posts/x.md # succeeds once the lock is free
Defensive patterns
Strategy: retry
Validate before calling
// Check the lock file is writable before attempting NewContent.
func CheckBuildLockWritable(workDir string) error {
p := filepath.Join(workDir, ".hugo_build.lock")
f, err := os.OpenFile(p, os.O_RDWR|os.O_CREATE, 0o644)
if err != nil {
return err
}
return f.Close()
} Try / catch
// Retry briefly on lock contention; fail fast on permission errors.
var lastErr error
for i := 0; i < 3; i++ {
err := create.NewContent(h, kind, path, force)
if err == nil { return nil }
lastErr = err
if !strings.Contains(err.Error(), "build lock") { return err }
// back off via a timer/channel; do not spin
}
return lastErr Prevention
- Do not run multiple Hugo writers against one project concurrently.
- Ensure the project root and .hugo_build.lock are writable by the Hugo user.
- Clean up stale lock files after a crash.
- Set noBuildLock for ephemeral/programmatic runs that do not need locking.
When it happens
Trigger: Another Hugo process (hugo server / hugo build / another hugo new) currently holds .hugo_build.lock; the project working dir or the lock file is not writable; a previously killed process left an OS-level lock; running on a read-only filesystem/container mount.
Common situations: Running `hugo new` concurrently with `hugo server`; CI running on a read-only checkout; stale lock after kill -9; lock file owned by another user.
Related errors
- failed to acquire a build lock
- abort: jekyll root contains neither posts nor drafts
- archetype directory ( ) not supported
- build filesystems
- could not determine content directory for
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/596638430510b0e0.
Report an issue: GitHub.
Appendix: source
Thrown at create/content.go:87
sourceFs: h.PathSpec.Fs.Source,
ps: h.PathSpec,
h: h,
cf: cf,
kind: kind,
targetPath: targetPath,
force: force,
}
ext := paths.Ext(targetPath)
b.setArcheTypeFilenameToUse(ext)
withBuildLock := func() (string, error) {
if !h.Configs.Base.NoBuildLock {
unlock, err := h.BaseFs.LockBuild()
if err != nil {
return "", fmt.Errorf("failed to acquire a build lock: %s", err)
}
defer unlock()
}
if b.isDir {
return "", b.buildDir()
}
if ext == "" {
return "", fmt.Errorf("failed to resolve %q to an archetype template", targetPath)
}
if !h.Conf.ContentTypes().IsContentFile(b.targetPath) {
return "", fmt.Errorf("target path %q is not a known content format", b.targetPath)
}
return b.buildFile()
}View on GitHub (pinned to 52c9bd7908)