{"id":"40357a179fb7f1de","repo":"gohugoio/hugo","slug":"key-must-start-with-q","errorCode":null,"errorMessage":"key must start with '/': %q","messagePattern":"key must start with '/': %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hugolib/doctree/support.go","lineNumber":118,"sourceCode":"\n// StopPropagation stops the propagation of the event.\nfunc (e *Event[T]) StopPropagation() {\n\te.stopPropagation = true\n}\n\n// ValidateKey returns an error if the key is not valid.\nfunc ValidateKey(key string) error {\n\tif key == \"\" {\n\t\t// Root node.\n\t\treturn nil\n\t}\n\n\tif len(key) < 2 {\n\t\treturn fmt.Errorf(\"too short key: %q\", key)\n\t}\n\n\tif key[0] != '/' {\n\t\treturn fmt.Errorf(\"key must start with '/': %q\", key)\n\t}\n\n\tif key[len(key)-1] == '/' {\n\t\treturn fmt.Errorf(\"key must not end with '/': %q\", key)\n\t}\n\n\treturn nil\n}\n\n// Event is used to communicate events in the tree.\ntype Event[T any] struct {\n\tName            string\n\tPath            string\n\tSource          T\n\tstopPropagation bool\n}\n\ntype LockType int","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/hugolib/doctree/support.go#L100-L136","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Prepend '/' or, better, use Hugo's path parsing helpers so keys are always rooted logical paths.","Trace where the key is built and normalize once at that boundary rather than at each call site."],"exampleFix":"// before\ntree.Insert(section+\"/\"+slug, n)\n// after\ntree.Insert(\"/\"+section+\"/\"+slug, n)","handlingStrategy":"validation","validationCode":"if !strings.HasPrefix(key, \"/\") {\n    key = \"/\" + key\n}","typeGuard":"func isValidTreeKey(key string) bool {\n    return len(key) >= 2 && strings.HasPrefix(key, \"/\") && !strings.HasSuffix(key, \"/\")\n}","tryCatchPattern":null,"preventionTips":["Always build keys from a root-relative path with an explicit leading slash","Use path.Join(\"/\", segments...) so the leading '/' is guaranteed","Assert isValidTreeKey(key) in tests covering every code path that constructs keys","Avoid concatenating strings manually for tree keys; use one shared key-builder helper"],"tags":["hugo","doctree","validation","paths"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}