gohugoio/hugo · error

key must start with '/': %q

Error message

key must start with '/': %q

What it means

doctree.ValidateKey requires every non-root key to be rooted, i.e. begin with '/'. Hugo's doctree is a path-keyed radix tree over logical page paths ("/blog/post"); a relative key would silently land in the wrong part of the tree, so unrooted keys are rejected up front.

Source

Thrown at hugolib/doctree/support.go:118

// StopPropagation stops the propagation of the event.
func (e *Event[T]) StopPropagation() {
	e.stopPropagation = true
}

// ValidateKey returns an error if the key is not valid.
func ValidateKey(key string) error {
	if key == "" {
		// Root node.
		return nil
	}

	if len(key) < 2 {
		return fmt.Errorf("too short key: %q", key)
	}

	if key[0] != '/' {
		return fmt.Errorf("key must start with '/': %q", key)
	}

	if key[len(key)-1] == '/' {
		return fmt.Errorf("key must not end with '/': %q", key)
	}

	return nil
}

// Event is used to communicate events in the tree.
type Event[T any] struct {
	Name            string
	Path            string
	Source          T
	stopPropagation bool
}

type LockType int

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Prepend '/' or, better, use Hugo's path parsing helpers so keys are always rooted logical paths.
  2. Trace where the key is built and normalize once at that boundary rather than at each call site.

Example fix

// before
tree.Insert(section+"/"+slug, n)
// after
tree.Insert("/"+section+"/"+slug, n)
Defensive patterns

Strategy: validation

Validate before calling

if !strings.HasPrefix(key, "/") {
    key = "/" + key
}

Type guard

func isValidTreeKey(key string) bool {
    return len(key) >= 2 && strings.HasPrefix(key, "/") && !strings.HasSuffix(key, "/")
}

Prevention

When it happens

Trigger: Inserting or looking up a doctree node with a key like "blog/post" instead of "/blog/post" — e.g. building the key from path segments without prepending the leading slash.

Common situations: Internal Hugo development or embedding hugolib and feeding filesystem-relative paths straight into the tree; forgetting to run paths through Hugo's path normalization (paths.Parse) which produces rooted paths.

Related errors


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