gohugoio/hugo · error
too many YAML aliases for non-scalar nodes
Error message
too many YAML aliases for non-scalar nodes
What it means
After YAML unmarshaling, Hugo counts how often each non-scalar (map/slice) node is referenced via YAML aliases and fails if any exceeds a size-scaled limit (100 for files under ~2 KB, up to 10,000 for larger files). This defends against Billion-Laughs-style expansion attacks: goccy/go-yaml itself isn't vulnerable at parse time, but rendering the massively self-referencing structure later (e.g. via RenderString) would be (see goccy/go-yaml issue 461).
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 8a468df065)
Solutions
- Reduce alias usage: expand the shared anchor inline for some entries, or restructure so each row stores only its own data and the shared defaults are merged in templates instead.
- Split one small alias-dense file into larger or multiple files — the limit scales with file size (100/<2KB, 5000/<10KB, 10000 above), so padding legitimate data into a larger file raises the cap.
- Convert the data file to JSON or TOML, which have no aliases and thus bypass the check.
- If the file is untrusted input, treat this as the guard working correctly — do not attempt to bypass it.
Example fix
# before (small file, one anchor aliased hundreds of times)
defaults: &d {a: 1, b: 2}
rows:
- *d
- *d
# ... 200 more
# after (store rows plainly; merge defaults in the template)
defaults: {a: 1, b: 2}
rows:
- {}
- {} Defensive patterns
Strategy: validation
Validate before calling
# Pre-scan YAML for alias-expansion abuse before feeding it to Hugo grep -c '\*[A-Za-z0-9_]' data.yaml # large counts of aliases on mappings/sequences are suspect
Try / catch
if err != nil && strings.Contains(err.Error(), "too many YAML aliases") {
// reject the document as untrusted (billion-laughs style); do not raise limits
} Prevention
- Treat this as a security guard against billion-laughs YAML bombs — don't work around it
- Avoid YAML anchors/aliases on maps and lists in your own data files; duplicate the data or restructure
- Never feed untrusted user-supplied YAML into Hugo data files without pre-validation
When it happens
Trigger: Parsing YAML front matter, data files, or config where a single anchored map or sequence (&anchor) is referenced with *alias more times than calculateCollectionAliasLimit allows for the file's size — most aggressively in small files (<2 KB, limit 100), which is where malicious payloads live.
Common situations: Legitimate data files that use one shared anchor (e.g. a common defaults map) across thousands of rows in a small file; security scanners or fuzzers feeding Billion Laughs payloads; content pipelines that generate alias-heavy YAML. Introduced alongside the goccy/go-yaml migration in Hugo v0.152.0, so files that worked on older Hugo may newly fail.
Related errors
- failed to load data: %w
- failed to parse file %q: %s
- failed to unmarshal config for path %q: %w
- failed to compile whitelist pattern %q: %w
- failed to load config: %w
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/6430f203e5d182be.json.
Report an issue: GitHub ↗.