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

  1. Strip the scheme+host from plink before calling; pass only the path portion.
  2. In frontmatter, use relative paths for the `url` field.
  3. 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

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


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)