gohugoio/hugo · error

too many YAML aliases for non-scalar nodes

Error message

too many YAML aliases for non-scalar nodes

What it means

Thrown by validateAliasLimitForCollections (parser/metadecoders/decoder.go:231) when a YAML document's non-scalar nodes (maps/lists) are referenced via anchors/aliases more than a computed limit. This is Hugo's defense against the 'billion laughs' / 'delayed laughter' exponential-expansion attack: a small malicious YAML can reference collection anchors enough times to blow up memory when later rendered. The limit scales with file size (100 for <2KB, 5000 for <10KB, 10000 otherwise).

Solutions

  1. Reduce the number of collection aliases in the YAML by inlining repeated structures or splitting the file.
  2. If the data is legitimately large and trusted, convert it to JSON (no anchors) to bypass the YAML alias validation entirely.
  3. For very large legitimate shared-row data, consider generating the data programmatically rather than via YAML aliases.
  4. Audit the file for accidental anchor loops introduced by a templating/export tool.

Example fix

# before (over-aliased)
_base: &base { a: 1, b: 2 }
rows:
  - *base
  - *base
  # ... thousands more

# after (inline or use JSON)
rows:
  - { a: 1, b: 2 }
  - { a: 1, b: 2 }
Defensive patterns

Strategy: validation

Validate before calling

// Reject overly-aliased YAML before Hugo loads it (heuristic: count aliases).
func estimateAliasCount(data []byte) int {
    return bytes.Count(data, []byte("*"))
}

Try / catch

if err := metadecoders.UnmarshalYaml(data, &m); err != nil {
    if strings.Contains(err.Error(), "too many YAML aliases") {
        // Convert to JSON or inline structures to bypass the limit.
    }
    return err
}

Prevention

When it happens

Trigger: Loading a YAML front-matter or data file that uses anchors (&base) referenced by many aliases (*base) on maps/lists such that the count exceeds the size-based limit; a legitimately large data file with aggressive alias reuse; a malicious or malformed data file fed into Hugo.

Common situations: A large YAML data file under data/ where every row aliases a shared map (can exceed 1000+ refs); a third-party theme/module shipping an over-aliased config; processing untrusted user-supplied YAML.

Related errors


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

Appendix: source

Thrown at parser/metadecoders/decoder.go:231

// we can easily get a delayed laughter when we try to render this very big structure later,
// e.g. via RenderString.
func validateAliasLimitForCollections(v any, limit int) error {
	if limit <= 0 {
		limit = 1000
	}

	collectionRefCounts := make(map[uintptr]int)

	checkCollectionRef := func(v *any) error {
		// Conversion of a Pointer to a uintptr (but not back to Pointer) is considered safe.
		// See https://pkg.go.dev/unsafe#pkg-functions
		ptr := uintptr(unsafe.Pointer(v))
		if ptr == 0 {
			return nil
		}
		collectionRefCounts[ptr]++
		if collectionRefCounts[ptr] > limit {
			return fmt.Errorf("too many YAML aliases for non-scalar nodes")
		}
		return nil
	}

	var validate func(v any) error
	validate = func(v any) error {
		switch vv := v.(type) {
		case *map[string]any:
			if err := checkCollectionRef(&v); err != nil {
				return err
			}
			for _, vvv := range *vv {
				if err := validate(vvv); err != nil {
					return err
				}
			}
		case map[string]any:
			if err := checkCollectionRef(&v); err != nil {

View on GitHub (pinned to 52c9bd7908)