gohugoio/hugo · error
failed to copy %q to %q: %w
Error message
failed to copy %q to %q: %w
What it means
Returned by `hugo convert` with --output when `hugio.CopyDir` fails to mirror a source content directory into the output directory (commands/convert.go:247-248). Before writing converted files, Hugo copies each content dir wholesale (skipping the output dir itself to avoid recursion); any I/O failure during that recursive copy — unreadable source entries, unwritable destination, disk full — surfaces here with both paths.
Source
Thrown at commands/convert.go:248
skipDirs := make(map[string]bool)
relToOutputDir, err := filepath.Rel(contentDir, outputDirAbs)
if err == nil && relToOutputDir != ".." && !strings.HasPrefix(relToOutputDir, ".."+string(filepath.Separator)) {
skipDirs[filepath.Clean(outputDirAbs)] = true
}
relToOutputContentDir, err := filepath.Rel(contentDir, outputContentDirAbs)
if err == nil && relToOutputContentDir != ".." && !strings.HasPrefix(relToOutputContentDir, ".."+string(filepath.Separator)) {
skipDirs[filepath.Clean(outputContentDirAbs)] = true
}
var shouldCopy func(filename string) bool
if len(skipDirs) > 0 {
shouldCopy = func(filename string) bool {
return !skipDirs[filepath.Clean(filename)]
}
}
if err := hugio.CopyDir(hugofs.Os, contentDir, outputContentDirAbs, shouldCopy); err != nil {
return fmt.Errorf("failed to copy %q to %q: %w", contentDir, outputContentDirAbs, err)
}
}
return nil
}
func (c *convertCommand) convertContents(format metadecoders.Format) error {
if c.outputDir == "" && !c.unsafe {
return newUserError("Unsafe operation not allowed, use --unsafe or set a different output path")
}
if err := c.h.Build(hugolib.BuildCfg{SkipRender: true}); err != nil {
return err
}
site := c.h.Sites[0]
workingDir := c.h.Sites[0].Deps.Conf.WorkingDir() + string(filepath.Separator)View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Read the wrapped error after the two paths — it names the specific file and syscall failure; fix that file first.
- Remove or fix broken symlinks and unreadable files under the source content directory (`find content -xtype l`).
- Ensure the output directory is on a writable volume with sufficient free space and that no existing file blocks a directory Hugo needs to create.
- Re-run after fixing; the copy is per-content-dir, so one bad entry aborts that dir's copy.
Example fix
# before: content/shared -> ../missing (broken symlink) hugo convert toYAML -o out # after rm content/shared && hugo convert toYAML -o out
Defensive patterns
Strategy: try-catch
Validate before calling
src, err := os.Stat(from)
if err != nil {
return fmt.Errorf("source missing: %w", err)
}
if !src.Mode().IsRegular() {
return fmt.Errorf("%q is not a regular file", from)
} Try / catch
if err != nil {
var pathErr *fs.PathError
if errors.As(err, &pathErr) {
// identify which side (source read vs dest write) failed via pathErr.Path
}
} Prevention
- Confirm source and destination are on writable, non-full filesystems before bulk copies
- Don't run conversion while another process (editor, hugo server) holds the same files
- Treat copy failures as fatal for that file and re-run only the failed subset after fixing the cause
When it happens
Trigger: Running `hugo convert to* -o <dir>` where a file under a content directory can't be read (permissions, broken symlink, file vanished mid-copy) or the destination can't be created/written (permission denied, disk full, destination path blocked by an existing file).
Common situations: Broken symlinks inside content/ (common after cloning repos that used symlinked shared content); mixed-ownership files from Docker; output directory on a full or read-only volume; antivirus/sync tools locking files on Windows.
Related errors
- failed to save file %q:: %w
- failed to resolve output path %q: %w
- %q is not a directory
- error processing file %q
- target path "%s" exists but is not a directory
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/bac84eb95295a190.json.
Report an issue: GitHub ↗.