{"id":"0eb018c527c52668","repo":"gohugoio/hugo","slug":"filepath-must-be-absolute","errorCode":null,"errorMessage":"filepath must be absolute","messagePattern":"filepath must be absolute","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/paths/url.go","lineNumber":123,"sourceCode":"// URLEscape escapes unicode letters.\nfunc URLEscape(uri string) string {\n\t// escape unicode letters\n\tu, err := url.Parse(uri)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn u.String()\n}\n\n// TrimExt trims the extension from a path..\nfunc TrimExt(in string) string {\n\treturn strings.TrimSuffix(in, path.Ext(in))\n}\n\n// From https://github.com/golang/go/blob/e0c76d95abfc1621259864adb3d101cf6f1f90fc/src/cmd/go/internal/web/url.go#L45\nfunc UrlFromFilename(filename string) (*url.URL, error) {\n\tif !filepath.IsAbs(filename) {\n\t\treturn nil, fmt.Errorf(\"filepath must be absolute\")\n\t}\n\n\t// If filename has a Windows volume name, convert the volume to a host and prefix\n\t// per https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/.\n\tif vol := filepath.VolumeName(filename); vol != \"\" {\n\t\tif strings.HasPrefix(vol, `\\\\`) {\n\t\t\tfilename = filepath.ToSlash(filename[2:])\n\t\t\ti := strings.IndexByte(filename, '/')\n\n\t\t\tif i < 0 {\n\t\t\t\t// A degenerate case.\n\t\t\t\t// \\\\host.example.com (without a share name)\n\t\t\t\t// becomes\n\t\t\t\t// file://host.example.com/\n\t\t\t\treturn &url.URL{\n\t\t\t\t\tScheme: \"file\",\n\t\t\t\t\tHost:   filename,\n\t\t\t\t\tPath:   \"/\",","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/common/paths/url.go#L105-L141","documentation":"`paths.UrlFromFilename` converts an OS filesystem path into a `file://` URL (code adapted from the Go toolchain). It requires the input to be an absolute path because a relative path cannot be turned into a well-defined file URL, and Windows volume/UNC handling depends on the absolute form. A relative input returns this error immediately.","triggerScenarios":"Any internal caller passing a relative path to `UrlFromFilename`, e.g. building source-map or file:// references from a path that wasn't run through `filepath.Abs` first; paths produced from working-directory-relative config values.","commonSituations":"Running Hugo from an unexpected working directory so a path stays relative; custom tooling or tests calling helpers with `content/post.md` instead of an absolute path; symlinked project layouts where path normalization was skipped.","solutions":["Resolve the path with `filepath.Abs` (or join it against the project/working dir) before calling `UrlFromFilename`.","Check where the path originates — config values and CLI args should be absolutized at the boundary.","In tests, construct paths with `t.TempDir()` or `filepath.Abs` rather than literals."],"exampleFix":"// before\nu, err := paths.UrlFromFilename(\"content/post.md\")\n\n// after\nabs, _ := filepath.Abs(\"content/post.md\")\nu, err := paths.UrlFromFilename(abs)","handlingStrategy":"validation","validationCode":"if !filepath.IsAbs(p) {\n    p = filepath.Join(baseDir, p)\n}","typeGuard":"func ensureAbs(p, base string) string {\n    if filepath.IsAbs(p) { return p }\n    return filepath.Join(base, p)\n}","tryCatchPattern":null,"preventionTips":["Convert paths with filepath.Abs or join against a known base before calling URL-from-filepath helpers","Never pass user-supplied or config-relative paths straight through; normalize at the boundary","Beware platform differences: a Windows drive path is not absolute on Linux"],"tags":["hugo","filesystem","urls","go"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}