gohugoio/hugo · error

failed to decode related config

Error message

failed to decode related config: %w

What it means

Specific to the `related` decoder (alldecoders.go:352), which only runs when a `[related]` section is present. It calls related.DecodeConfig, which rejects: nil/empty related config, threshold outside 0-100, an invalid index `type` (must be a known type), and cardinalityThreshold outside 0-100. Each becomes the wrapped cause.

Solutions

  1. Set `threshold` between 0 and 100 inclusive.
  2. Use only valid index types: the wrapped error lists them via maps.Keys(validTypes).
  3. If you don't need a custom related config, delete the `[related]` section to use the default (auto-adds a tags index).
  4. Ensure each `[[related.indices]]` has a `name` and a valid `type`.

Example fix

# before
[related]
threshold = 120
[[related.indices]]
name = "tags"
type = "fuzzy"

# after
[related]
threshold = 80
[[related.indices]]
name = "tags"
weight = 80
type = "basic"
Defensive patterns

Strategy: validation

Validate before calling

python3 - <<'PY'
import tomllib
cfg=tomllib.loads(open('hugo.toml').read())
r=cfg.get('related')
if r is not None:
    assert r, "related section is empty"
    assert 0<=r.get('threshold',0)<=100, "threshold out of range"
    valid={'basic'}
    for idx in r.get('indices',[]):
        t=idx.get('type','basic')
        assert t in valid, f"invalid index type {t!r}"
print("related ok")
PY

Prevention

When it happens

Trigger: Providing `[related]` as an empty table; setting `threshold = 150` or a negative; declaring an index with `type = "fuzzy"` (not a valid type); setting an index `cardinalityThreshold` outside 0-100.

Common situations: Copying a related config from an old Hugo version where valid types differed; partially editing a working related config and leaving an empty body; misreading docs and using an unsupported index type.

Understand the failure class

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/da111fb8c783e624. Report an issue: GitHub.

Appendix: source

Thrown at config/allconfig/alldecoders.go:360

				for k, v := range m {
					if k == "" || v == "" {
						delete(m, k)
					}
				}
				p.c.Taxonomies = m
			}
			return nil
		},
	},
	"related": {
		key:    "related",
		weight: 100, // This needs to be decoded after taxonomies.
		decode: func(d decodeWeight, p decodeConfig) error {
			if p.p.IsSet(d.key) {
				var err error
				p.c.Related, err = related.DecodeConfig(p.p.GetParams(d.key))
				if err != nil {
					return fmt.Errorf("failed to decode related config: %w", err)
				}
			} else {
				p.c.Related = related.DefaultConfig
				if _, found := p.c.Taxonomies["tag"]; found {
					p.c.Related.Add(related.IndexConfig{Name: "tags", Weight: 80, Type: related.TypeBasic})
				}
			}
			return nil
		},
	},

	"cascade": {
		key: "cascade",
		decode: func(d decodeWeight, p decodeConfig) error {
			var err error
			p.c.Cascade, err = page.DecodeCascadeConfig(p.p.Get(d.key))
			return err
		},

View on GitHub (pinned to 52c9bd7908)