gohugoio/hugo · error

key must not end with '/': %q

Error message

key must not end with '/': %q

What it means

doctree.ValidateKey forbids trailing slashes on keys. Keys are canonical node identifiers ("/blog/post"); allowing "/blog/post/" would create two distinct keys for the same logical node and break prefix-based tree walks, so the trailing-slash form is rejected.

Source

Thrown at hugolib/doctree/support.go:122

}

// 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

// MutableTree is a tree that can be modified.
type MutableTree interface {
	DeleteRaw(key string)

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. strings.TrimSuffix(key, "/") before use, or fix the join that appends the empty segment.
  2. Don't use rendered URLs as tree keys — use the logical page path from Hugo's path helpers.

Example fix

// before
tree.Insert(dir+"/", n)
// after
tree.Insert(strings.TrimSuffix(dir, "/"), n)
Defensive patterns

Strategy: validation

Validate before calling

key = strings.TrimSuffix(key, "/")
if key == "" {
    key = "/" // root is the only key allowed to be just '/'
}

Type guard

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

Prevention

When it happens

Trigger: Inserting or querying with a key ending in '/', e.g. "/blog/" — typically from joining a directory path with an empty segment, or passing a URL-style path (which conventionally keeps trailing slashes) directly as a tree key.

Common situations: Internal Hugo development or hugolib embedding: converting permalinks/URLs (which end in '/') into doctree keys without trimming; string concatenation like dir + "/" + name where name is empty.

Related errors


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