gohugoio/hugo · error

could not determine content directory for %q

Error message

could not determine content directory for %q

What it means

`BaseFs.AbsProjectContentDir` maps a content filename (as given to `hugo new`) to an absolute path inside one of the project's content mounts. If the name doesn't fall under any configured content mount and no project-owned content directory exists to place it in, Hugo cannot decide where the file belongs and returns this error.

Source

Thrown at hugolib/filesystems/basefs.go:210

	}

	if !isAbs {
		// A filename on the form "posts/mypage.md", put it inside
		// the first content folder, usually <workDir>/content.
		// Pick the first project dir (which is probably the most important one).
		for _, dir := range b.SourceFilesystems.Content.mounts() {
			if !dir.IsDir() {
				continue
			}
			meta := dir.Meta()
			if meta.IsProject {
				return filename, filepath.Join(meta.Filename, filename), nil
			}
		}
	}

	return "", "", fmt.Errorf("could not determine content directory for %q", filename)
}

// ResolveJSConfigFile resolves the JS-related config file to a absolute
// filename. One example of such would be postcss.config.js.
func (b *BaseFs) ResolveJSConfigFile(name string) string {
	// First look in assets/_jsconfig
	fi, err := b.Assets.Fs.Stat(filepath.Join(files.FolderJSConfig, name))
	if err == nil {
		return fi.(hugofs.FileMetaInfo).Meta().Filename
	}
	// Fall back to the work dir.
	fi, err = b.Work.Stat(name)
	if err == nil {
		return fi.(hugofs.FileMetaInfo).Meta().Filename
	}

	return ""
}

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Run the command from the site root and ensure the `content/` directory (or your configured `contentDir`) exists — `mkdir content` fixes the fresh-site case.
  2. If you use `[[module.mounts]]`, add a project mount with `source = "content"` and `target = "content"` so there's a project content dir to receive new files.
  3. Pass a path that resolves inside an existing content mount, e.g. `hugo new content posts/first.md` rather than an absolute path outside the project.

Example fix

# before (hugo.toml with custom mounts, no project content mount)
[[module.mounts]]
source = "themes/x/content"
target = "content"
# after
[[module.mounts]]
source = "content"
target = "content"
[[module.mounts]]
source = "themes/x/content"
target = "content"
Defensive patterns

Strategy: validation

Validate before calling

# Verify the target directory maps into a configured content mount:
hugo config mounts | jq '.[] | select(.target|startswith("content"))'
# Ensure the path you create/reference lives under one of those source dirs

Try / catch

if err := createContent(path); err != nil {
    return fmt.Errorf("path %q is outside all content mounts — add a [[module.mounts]] entry or use contentDir: %w", path, err)
}

Prevention

When it happens

Trigger: Running `hugo new content posts/my-page.md` (or the archetype machinery) when the resolved path matches no content mount: the `content` directory is missing, all content mounts come from themes/modules (none has `IsProject`), or an absolute/target path lies outside every configured `[[module.mounts]]` source for the content component.

Common situations: Running `hugo new` from the wrong working directory (not the site root); a config using `[[module.mounts]]` that remaps content but forgets a project-level content mount; brand-new sites where `content/` was never created; `contentDir` pointing at a non-existent path.

Related errors


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