gohugoio/hugo · error

failed to save file %q:: %w

Error message

failed to save file %q:: %w

What it means

Returned by `hugo convert` when the converted page cannot be written back to disk via `helpers.WriteToDisk` (commands/convert.go:198-199). Parsing and re-encoding succeeded; the failure is purely at the filesystem write step for the computed target filename (in place, or under --output). The doubled colon is a formatting quirk in the source.

Source

Thrown at commands/convert.go:199

	if err != nil {
		site.Log.Errorln(errMsg)
		return err
	}

	newContent.Write(pf.Content)

	newFilename := p.File().Filename()

	if c.outputDir != "" {
		contentDir := strings.TrimSuffix(newFilename, p.File().Path())
		contentDir = filepath.Base(contentDir)

		newFilename = filepath.Join(c.outputDir, contentDir, p.File().Path())
	}

	fs := hugofs.Os
	if err := helpers.WriteToDisk(newFilename, &newContent, fs); err != nil {
		return fmt.Errorf("failed to save file %q:: %w", newFilename, err)
	}

	return nil
}

func (c *convertCommand) copyContentDirsForOutput(pagesBackedByFile page.Pages) error {
	contentDirs := make(map[string]bool)
	for _, p := range pagesBackedByFile {
		filename := p.File().Filename()
		contentDir := strings.TrimSuffix(filename, p.File().Path())
		if contentDir == filename {
			continue
		}
		contentDirs[filepath.Clean(contentDir)] = true
	}

	var contentDirList []string
	for contentDir := range contentDirs {

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Check permissions on the reported path and its parent directory; ensure the user running hugo can write there (`ls -l`, chown/chmod as needed).
  2. If using --unsafe in-place conversion on a read-only mount, switch to `-o/--output <writable-dir>` instead.
  3. Verify free disk space and that no path component of the target is an existing regular file.
  4. On Windows, close editors/sync clients holding the file and re-run.

Example fix

# before
hugo convert toYAML --unsafe   # content/ is a read-only mount
# after
hugo convert toYAML -o /tmp/converted
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the output directory is writable before converting
f, err := os.CreateTemp(outputDir, ".writecheck*")
if err != nil {
    return fmt.Errorf("output dir not writable: %w", err)
}
f.Close()
os.Remove(f.Name())

Try / catch

if err != nil {
    var pathErr *fs.PathError
    if errors.As(err, &pathErr) {
        // permission/disk issue: report path and underlying errno, abort before more writes
    }
}

Prevention

When it happens

Trigger: Running `hugo convert to*` where the target file or its parent directory is not writable: read-only content files or mounts, insufficient permissions on the --output directory, a full disk, a path component that exists as a file, or on Windows a file locked by another process (editor/sync client).

Common situations: Converting inside a Docker container with a read-only bind mount; content files checked out read-only; --output pointing at a protected system path; OneDrive/Dropbox locking files during write; disk-full CI runners.

Related errors


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