{"record":{"id":"f624fedc422a1b01","repo":"gohugoio/hugo","slug":"index-q-not-valid","errorCode":null,"errorMessage":"index %q not valid","messagePattern":"index %q not valid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"related/inverted_index.go","lineNumber":393,"sourceCode":"\t\t\tfor _, fragment := range opts.Fragments {\n\t\t\t\tkeywords = append(keywords, FragmentKeyword(fragment))\n\t\t\t}\n\t\t\tif opts.Document != nil {\n\t\t\t\tif fp, ok := opts.Document.(FragmentProvider); ok {\n\t\t\t\t\tfor _, fragment := range fp.Fragments(ctx).Identifiers {\n\t\t\t\t\t\tkeywords = append(keywords, FragmentKeyword(fragment))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tqueryElements = append(queryElements, newQueryElement(cfg.Name, keywords...))\n\t}\n\tfor _, slice := range opts.NamedSlices {\n\t\tvar keywords []Keyword\n\t\tkey := slice.KeyString()\n\t\tif key == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"index %q not valid\", slice.Key)\n\t\t}\n\t\tconf, found := idx.getIndexCfg(key)\n\t\tif !found {\n\t\t\treturn nil, fmt.Errorf(\"index %q not found\", key)\n\t\t}\n\n\t\tfor _, val := range slice.Values {\n\t\t\tk, err := conf.ToKeywords(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tkeywords = append(keywords, k...)\n\t\t}\n\t\tqueryElements = append(queryElements, newQueryElement(conf.Name, keywords...))\n\t}\n\n\tif opts.Document != nil {\n\t\treturn idx.searchDate(ctx, opts.Document, opts.Document.PublishDate(), queryElements...)","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/related/inverted_index.go#L375-L411","documentation":"Returned by InvertedIndex.Search when a NamedSlice in opts.NamedSlices has a key that resolves to an empty string via KeyString(). Each named query element must carry a non-empty index key; a blank key means the caller passed an unkeyed slice, which the search cannot route to an index.","triggerScenarios":"Constructing SearchOpts.NamedSlices with a types.KeyValues whose Key is nil/unset (so KeyString() returns the empty string), e.g. passing a positional slice instead of a map-keyed value when building a related query programmatically.","commonSituations":"Programmatic API misuse where a developer builds types.KeyValues without setting the Key field; a serialization round-trip that drops the key; or a template helper emitting an empty index name.","solutions":["Ensure every types.KeyValues in NamedSlices has a non-empty Key matching a configured index name.","Validate the key before constructing SearchOpts and skip/log entries with empty keys.","Check the upstream code that builds the named slices to confirm keys are always set."],"exampleFix":"// before: KeyValues built without a key\nopts := related.SearchOpts{\n  NamedSlices: []types.KeyValues{{Values: []any{\"go\"}}},\n}\n\n// after\nopts := related.SearchOpts{\n  NamedSlices: []types.KeyValues{{Key: \"keywords\", Values: []any{\"go\"}}},\n}","handlingStrategy":"validation","validationCode":"// Filter out NamedSlices with empty keys before constructing SearchOpts.\nfunc filterEmptyKeys(kv []types.KeyValues) []types.KeyValues {\n    out := kv[:0]\n    for _, e := range kv {\n        if e.KeyString() != \"\" { out = append(out, e) }\n    }\n    return out\n}","typeGuard":null,"tryCatchPattern":"if _, err := idx.Search(ctx, opts); err != nil {\n    if strings.Contains(err.Error(), \"not valid\") { /* empty key -- fix builder */ }\n}","preventionTips":["Always set Key when constructing types.KeyValues.","Never pass positional (unkeyed) slices to NamedSlices.","Unit-test the SearchOpts builder for non-empty keys."],"tags":["related","search","validation","named-slices"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}