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

  1. Read the wrapped error after the two paths — it names the specific file and syscall failure; fix that file first.
  2. Remove or fix broken symlinks and unreadable files under the source content directory (`find content -xtype l`).
  3. 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.
  4. 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

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


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/bac84eb95295a190.json. Report an issue: GitHub ↗.