gohugoio/hugo · error

key %q not allowed in cascade config

Error message

key %q not allowed in cascade config

What it means

Cascade entries are decoded in resources/page/page_matcher.go, and any key present in `disallowedCascadeKeys` — `kind`, `path`, `lang`, and `cascade` — is rejected at the top level of a cascade block. These four keys define the structure of the page tree (or would recurse), so Hugo cannot let a cascade rewrite them on descendant pages. Note they *are* valid inside a cascade's `_target` matcher; only the value side is forbidden.

Source

Thrown at resources/page/page_matcher.go:161

		if err != nil {
			return CascadeConfig{}, nil, err
		}

		var cfgs []PageMatcherParamsConfig

		for _, m := range ms {
			m = hmaps.CleanConfigStringMap(m)
			var (
				c   PageMatcherParamsConfig
				err error
			)
			c, err = dec.mapToPageMatcherParamsConfig(m)
			if err != nil {
				return CascadeConfig{}, nil, err
			}
			for k := range m {
				if disallowedCascadeKeys[k] {
					return CascadeConfig{}, nil, fmt.Errorf("key %q not allowed in cascade config", k)
				}
			}
			cfgs = append(cfgs, c)
		}

		if len(cfgs) == 0 {
			return CascadeConfig{}, nil, nil
		}

		var n int
		for _, cfg := range cfgs {
			if len(cfg.Params) > 0 || len(cfg.Fields) > 0 {
				cfgs[n] = cfg
				n++
			}
		}

		if n == 0 {

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Move `kind`, `path`, and `lang` into the cascade's `_target` block — that's where they act as matchers.
  2. Delete any nested `cascade:` key; cascade already propagates down the tree, so nesting is unnecessary and disallowed.
  3. If you truly need to set a page's kind or language, do it on the page itself (front matter / filename language suffix), not via cascade.
  4. Run `hugo config` to see the decoded cascade and confirm the shape matches `_target` + params.

Example fix

# before
cascade:
  kind: section
  banner: hero.jpg

# after
cascade:
  - _target:
      kind: section
    banner: hero.jpg
Defensive patterns

Strategy: validation

Validate before calling

// only use allowed keys in a cascade _target/params block
allowed := map[string]bool{"path": true, "kind": true, "lang": true, "environment": true}
for k := range targetMap {
    if !allowed[strings.ToLower(k)] {
        return fmt.Errorf("cascade target key %q not allowed", k)
    }
}

Prevention

When it happens

Trigger: Writing `cascade:` in front matter or `[[cascade]]` in site config with `kind:`, `path:`, `lang:`, or a nested `cascade:` as a sibling of `_target`/params, instead of placing `kind`/`path`/`lang` inside `_target`.

Common situations: Confusing the matcher and the payload: intending to restrict a cascade to `kind: section` but putting `kind` at the cascade level rather than under `_target`; attempting nested cascades to build multi-level inheritance; copying a `_target` block from docs and dropping the `_target` wrapper.

Related errors


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