gohugoio/hugo · error
error copying static files
Error message
error copying static files: %w
What it means
During `fullBuild`, Hugo runs `c.copyStatic()` concurrently with site building. This error wraps any failure from copying the project's `static/` directory (and theme static dirs) into the publish directory. It surfaces I/O, symlink, or filesystem-layer problems rather than template/content problems.
Solutions
- Inspect the wrapped underlying error to locate the offending path.
- Check permissions and resolve any broken symlinks under `static/` and theme static dirs.
- Confirm the configured `publishDir` is writable and has free space.
- Validate custom filesystem mounts in `hugo.toml` (the `[[mounts]]` blocks) point at real directories.
Example fix
// before // static/img -> ../../missing (broken symlink) // after // rm static/img && cp -r ../real/imgs static/img
Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: walk the static dir and verify every file is readable;
// check the publishDir is writable.
func validateStatic(staticDir, publishDir string) error {
return filepath.Walk(staticDir, func(p string, fi os.FileInfo, err error) error {
if err != nil { return err }
if fi.IsDir() { return nil }
f, e := os.Open(p); if e != nil { return e }
return f.Close()
})
} Type guard
null
Try / catch
// On copyStatic failure, surface the offending path from the wrapped error.
var pathErr *os.PathError
if errors.As(err, &pathErr) {
log.Printf("static copy failed at %s: %v", pathErr.Path, pathErr)
} Prevention
- Symlink-check `static/` and theme static dirs before building (`find -L static -type l`).
- Confirm `publishDir` is on a writable volume with free space.
- Verify `[[mounts]]` source paths in `hugo.toml` exist.
When it happens
Trigger: Fires when `copyStatic` returns non-nil — e.g. a static file is unreadable, a symlink loop exists, the destination is on a read-only filesystem, or a custom Hugo filesystem mount is misconfigured.
Common situations: A broken symlink or permission-restricted file under `static/`; a `publishDir` set to a read-only or out-of-space location; misconfigured module/theme static mounts pointing at non-existent paths; running out of inode/disk space during copy.
Related errors
- abort: jekyll root contains neither posts nor drafts
- assemble
- build filesystems
- could not determine content directory for
- create main fs
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/50841e8fec019b0f.
Report an issue: GitHub.
Appendix: source
Thrown at commands/hugobuilder.go:577
func (c *hugoBuilder) fullBuild(noBuildLock bool) error {
var (
g errgroup.Group
langCount map[string]uint64
)
c.r.logger.Println("Start building sites … ")
c.r.logger.Println(hugo.BuildVersionString())
c.r.logger.Println()
if terminal.IsTerminal(os.Stdout) {
defer func() {
fmt.Print(showCursor + clearLine)
}()
}
copyStaticFunc := func() error {
cnt, err := c.copyStatic()
if err != nil {
return fmt.Errorf("error copying static files: %w", err)
}
langCount = cnt
return nil
}
buildSitesFunc := func() error {
if err := c.buildSites(noBuildLock); err != nil {
return fmt.Errorf("error building site: %w", err)
}
return nil
}
// Do not copy static files and build sites in parallel if cleanDestinationDir is enabled.
// This flag deletes all static resources in /public folder that are missing in /static,
// and it does so at the end of copyStatic() call.
var cleanDestinationDir bool
c.withConf(func(conf *commonConfig) {
cleanDestinationDir = conf.configs.Base.CleanDestinationDir
})
if cleanDestinationDir {View on GitHub (pinned to 52c9bd7908)