caddyserver/caddy · error

unterminated front matter

Error message

unterminated front matter

What it means

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.

Source

Thrown at modules/caddyhttp/templates/frontmatter.go:64

	if fmParser == nil {
		// no recognized front matter; whole document is body
		return nil, input, nil
	}

	// find end of front matter
	var fmEndFence string
	fmEndFenceStart := -1
	for _, fence := range closingFence {
		index := strings.Index(input[firstLineEnd:], "\n"+fence)
		if index >= 0 {
			fmEndFenceStart = index
			fmEndFence = fence
			break
		}
	}
	if fmEndFenceStart < 0 {
		return nil, "", fmt.Errorf("unterminated front matter")
	}
	fmEndFenceStart += firstLineEnd + 1 // add 1 to account for newline

	// extract and parse front matter
	frontMatter := input[firstLineEnd:fmEndFenceStart]
	fm, err := fmParser([]byte(frontMatter))
	if err != nil {
		return nil, "", err
	}

	// the rest is the body
	body := input[fmEndFenceStart+len(fmEndFence):]

	return fm, body, nil
}

func yamlFrontMatter(input []byte) (map[string]any, error) {
	m := make(map[string]any)

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Add the matching closing fence on its own line (--- for YAML, +++ for TOML, } for JSON)
  2. Ensure LF line endings and that the closing fence starts at column 0 with no leading spaces
  3. If the file has no front matter, do not pass it to split_frontmatter; branch with a contains/hasPrefix check first
  4. Test the document standalone with a YAML linter to confirm the front matter block is well formed

Example fix

<!-- before -->
---
title: Broken

No closing fence.

<!-- after -->
---
title: Fixed
---

Body text.
Defensive patterns

Strategy: validation

Validate before calling

{{ $hasFM := strings.HasPrefix .Content "---" }}
{{ if $hasFM }}{{ $fm := splitFrontMatter .Content }}{{ end }}

# authoring-side check (Go):
# if bytes.HasPrefix(b, []byte("---")) && !bytes.Contains(b[len(b):], []byte("\n---")) { return errors.New("missing closing fence") }

Prevention

When it happens

Trigger: 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 '+++'.

Common situations: 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.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/5f75babeabb2a90f. Report an issue: GitHub.