gohugoio/hugo · error

failed to resolve output path

Error message

failed to resolve output path %q: %w

What it means

In copyContentDirsForOutput (convert.go:222-225), filepath.Abs(c.outputDir) converts the user-provided output directory to an absolute path. If this fails — typically because the current working directory cannot be determined — the error is wrapped with the output path.

Solutions

  1. Provide a valid absolute --output path so filepath.Abs is a no-op
  2. Ensure the current working directory exists and is accessible
  3. Check the --output value for invalid path characters

Example fix

# before: cwd may be unavailable
hugo convert toYAML --output converted
# after: absolute path
hugo convert toYAML --output /home/user/converted
Defensive patterns

Strategy: validation

Validate before calling

// Resolve the output path to absolute and verify accessibility beforehand.
func resolveOutputPath(dir string) (string, error) {
    abs, err := filepath.Abs(dir)
    if err != nil {
        return "", fmt.Errorf("failed to resolve output path %q: %w", dir, err)
    }
    return abs, os.MkdirAll(abs, 0o755)
}

Prevention

When it happens

Trigger: The --output value cannot be made absolute, usually because os.Getwd fails (deleted cwd, permission denied on a parent), or the path contains invalid characters for the platform.

Common situations: Current working directory was deleted while Hugo runs; permission issues traversing the path to root; exotic/invalid path characters on Windows.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/80d2dc243e728636. Report an issue: GitHub.

Appendix: source

Thrown at commands/convert.go:224

	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 {
		contentDirList = append(contentDirList, contentDir)
	}
	slices.Sort(contentDirList)

	outputDirAbs, err := filepath.Abs(c.outputDir)
	if err != nil {
		return fmt.Errorf("failed to resolve output path %q: %w", c.outputDir, err)
	}

	for _, contentDir := range contentDirList {
		outputContentDirAbs := filepath.Join(outputDirAbs, filepath.Base(contentDir))

		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 {

View on GitHub (pinned to 52c9bd7908)