{"record":{"id":"6430f203e5d182be","repo":"gohugoio/hugo","slug":"too-many-yaml-aliases-for-non-scalar-nodes","errorCode":null,"errorMessage":"too many YAML aliases for non-scalar nodes","messagePattern":"too many YAML aliases for non-scalar nodes","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"parser/metadecoders/decoder.go","lineNumber":231,"sourceCode":"// we can easily get a delayed laughter when we try to render this very big structure later,\n// e.g. via RenderString.\nfunc validateAliasLimitForCollections(v any, limit int) error {\n\tif limit <= 0 {\n\t\tlimit = 1000\n\t}\n\n\tcollectionRefCounts := make(map[uintptr]int)\n\n\tcheckCollectionRef := func(v *any) error {\n\t\t// Conversion of a Pointer to a uintptr (but not back to Pointer) is considered safe.\n\t\t// See https://pkg.go.dev/unsafe#pkg-functions\n\t\tptr := uintptr(unsafe.Pointer(v))\n\t\tif ptr == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tcollectionRefCounts[ptr]++\n\t\tif collectionRefCounts[ptr] > limit {\n\t\t\treturn fmt.Errorf(\"too many YAML aliases for non-scalar nodes\")\n\t\t}\n\t\treturn nil\n\t}\n\n\tvar validate func(v any) error\n\tvalidate = func(v any) error {\n\t\tswitch vv := v.(type) {\n\t\tcase *map[string]any:\n\t\t\tif err := checkCollectionRef(&v); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfor _, vvv := range *vv {\n\t\t\t\tif err := validate(vvv); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\tcase map[string]any:\n\t\t\tif err := checkCollectionRef(&v); err != nil {","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/parser/metadecoders/decoder.go#L213-L249","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Reduce the number of collection aliases in the YAML by inlining repeated structures or splitting the file.","If the data is legitimately large and trusted, convert it to JSON (no anchors) to bypass the YAML alias validation entirely.","For very large legitimate shared-row data, consider generating the data programmatically rather than via YAML aliases.","Audit the file for accidental anchor loops introduced by a templating/export tool."],"exampleFix":"# before (over-aliased)\n_base: &base { a: 1, b: 2 }\nrows:\n  - *base\n  - *base\n  # ... thousands more\n\n# after (inline or use JSON)\nrows:\n  - { a: 1, b: 2 }\n  - { a: 1, b: 2 }","handlingStrategy":"validation","validationCode":"// Reject overly-aliased YAML before Hugo loads it (heuristic: count aliases).\nfunc estimateAliasCount(data []byte) int {\n    return bytes.Count(data, []byte(\"*\"))\n}","typeGuard":null,"tryCatchPattern":"if err := metadecoders.UnmarshalYaml(data, &m); err != nil {\n    if strings.Contains(err.Error(), \"too many YAML aliases\") {\n        // Convert to JSON or inline structures to bypass the limit.\n    }\n    return err\n}","preventionTips":["Prefer JSON over YAML for large data files with shared rows.","Inline repeated structures instead of aliasing.","Treat untrusted YAML as untrusted; sanitize before loading."],"tags":["yaml","security","metadecoders","billion-laughs","hugo"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}