gohugoio/hugo · critical
can't make permalink from absolute link
Error message
can't make permalink from absolute link %q
What it means
Panicked (not returned) by paths.MakePermalink when the supplied content link `plink` already contains a host component. MakePermalink joins a host base URL with a path; an absolute link would double the host, so it refuses rather than producing a malformed URL.
Solutions
- Strip the scheme+host from plink before calling; pass only the path portion.
- In frontmatter, use relative paths for the `url` field.
- If absolute URLs are required downstream, skip MakePermalink and use the link as-is.
Example fix
// before
paths.MakePermalink("https://site.com/", "https://site.com/post/x")
// after
paths.MakePermalink("https://site.com/", "/post/x") Defensive patterns
Strategy: validation
Validate before calling
func isRelativeLink(s string) bool {
u, err := url.Parse(s); if err != nil { return false }
return u.Host == ""
} Type guard
func isRelativeLink(s string) bool { u, err := url.Parse(s); return err == nil && u.Host == "" } Prevention
- Keep frontmatter url/permalink fields relative.
- Strip scheme+host from imported canonical URLs during migration.
- Wrap MakePermalink calls with a recover() in template pipelines.
When it happens
Trigger: Calling paths.MakePermalink(host, plink) where plink parses to a *url.URL with a non-empty .Host (e.g. 'https://example.com/post/x' instead of '/post/x').
Common situations: Frontmatter `url`/`permalink` fields set to absolute URLs, or content migrations that imported canonical URLs; Hugo's permalink construction then hits this panic during page rendering.
Related errors
- alignx must be one of left, center, right
- assemble
- content source: failed to convert %T to string
- ContentMediaType not set
- contentNodeToContentNodesPage: unexpected type %T
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/ff2e41ea17b6a558.
Report an issue: GitHub.
Appendix: source
Thrown at common/paths/url.go:71
// MakePermalink combines base URL with content path to create full URL paths.
// Example
//
// base: http://spf13.com/
// path: post/how-i-blog
// result: http://spf13.com/post/how-i-blog
func MakePermalink(host, plink string) *url.URL {
base, err := url.Parse(host)
if err != nil {
panic(err)
}
p, err := url.Parse(plink)
if err != nil {
panic(err)
}
if p.Host != "" {
panic(fmt.Errorf("can't make permalink from absolute link %q", plink))
}
base.Path = path.Join(base.Path, p.Path)
base.Fragment = p.Fragment
base.RawQuery = p.RawQuery
// path.Join will strip off the last /, so put it back if it was there.
hadTrailingSlash := (plink == "" && strings.HasSuffix(host, "/")) || strings.HasSuffix(p.Path, "/")
if hadTrailingSlash && !strings.HasSuffix(base.Path, "/") {
base.Path = base.Path + "/"
}
return base
}
// AddContextRoot adds the context root to an URL if it's not already set.
// For relative URL entries on sites with a base url with a context root set (i.e. http://example.com/mysite),
// relative URLs must not include the context root if canonifyURLs is enabled. But if it's disabled, it must be set.View on GitHub (pinned to 52c9bd7908)