{"record":{"id":"5f75babeabb2a90f","repo":"caddyserver/caddy","slug":"unterminated-front-matter","errorCode":null,"errorMessage":"unterminated front matter","messagePattern":"unterminated front matter","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/templates/frontmatter.go","lineNumber":64,"sourceCode":"\n\tif fmParser == nil {\n\t\t// no recognized front matter; whole document is body\n\t\treturn nil, input, nil\n\t}\n\n\t// find end of front matter\n\tvar fmEndFence string\n\tfmEndFenceStart := -1\n\tfor _, fence := range closingFence {\n\t\tindex := strings.Index(input[firstLineEnd:], \"\\n\"+fence)\n\t\tif index >= 0 {\n\t\t\tfmEndFenceStart = index\n\t\t\tfmEndFence = fence\n\t\t\tbreak\n\t\t}\n\t}\n\tif fmEndFenceStart < 0 {\n\t\treturn nil, \"\", fmt.Errorf(\"unterminated front matter\")\n\t}\n\tfmEndFenceStart += firstLineEnd + 1 // add 1 to account for newline\n\n\t// extract and parse front matter\n\tfrontMatter := input[firstLineEnd:fmEndFenceStart]\n\tfm, err := fmParser([]byte(frontMatter))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\t// the rest is the body\n\tbody := input[fmEndFenceStart+len(fmEndFence):]\n\n\treturn fm, body, nil\n}\n\nfunc yamlFrontMatter(input []byte) (map[string]any, error) {\n\tm := make(map[string]any)","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/templates/frontmatter.go#L46-L82","documentation":"Thrown by the templates module's front-matter parser (used by the split_frontmatter template function) when an opening fence (--- for YAML, + for TOML, { for JSON) is found but no matching closing fence appears before the end of input. The parser searches for '\\n---', '\\n+++', or '\\n}' after the first line.","triggerScenarios":"Calling {{ split_frontmatter .Content }} in a template where the document starts with '---' but never has a second '---' line; or a JSON front matter block whose closing '}' is not at the start of a line; or a TOML block missing the closing '+++'.","commonSituations":"Markdown files edited by hand where the closing fence was deleted, front matter authored with CRLF line endings so the '\\n-' search behaves unexpectedly, files where the opening fence is the only line, or content that legitimately begins with '---' (e.g. an hr) but is fed through split_frontmatter anyway.","solutions":["Add the matching closing fence on its own line (--- for YAML, +++ for TOML, } for JSON)","Ensure LF line endings and that the closing fence starts at column 0 with no leading spaces","If the file has no front matter, do not pass it to split_frontmatter; branch with a contains/hasPrefix check first","Test the document standalone with a YAML linter to confirm the front matter block is well formed"],"exampleFix":"<!-- before -->\n---\ntitle: Broken\n\nNo closing fence.\n\n<!-- after -->\n---\ntitle: Fixed\n---\n\nBody text.","handlingStrategy":"validation","validationCode":"{{ $hasFM := strings.HasPrefix .Content \"---\" }}\n{{ if $hasFM }}{{ $fm := splitFrontMatter .Content }}{{ end }}\n\n# authoring-side check (Go):\n# if bytes.HasPrefix(b, []byte(\"---\")) && !bytes.Contains(b[len(b):], []byte(\"\\n---\")) { return errors.New(\"missing closing fence\") }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Lint markdown sources in CI to require balanced front-matter fences","Use LF line endings for template sources","Only call split_frontmatter on files known to have front matter"],"tags":["caddy","templates","frontmatter","markdown"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}