gohugoio/hugo · critical

illegal state in content; shortcode token missing end delim

Error message

illegal state in content; shortcode token missing end delim

What it means

After markdown rendering, Hugo replaces placeholder tokens (prefix ... 'HBHB' end marker) in the rendered output with the actual shortcode results. This error means a token's start prefix was found but its 'HBHB' end delimiter was not — the placeholder was corrupted between insertion and expansion. It is an internal invariant violation ('should never happen'), not a user syntax error per se.

Source

Thrown at hugolib/shortcode.go:744

	ctx context.Context,
	source []byte,
	tokenHandler func(ctx context.Context, token string) ([]byte, error),
) ([]byte, error) {
	start := 0

	pre := []byte(shortcodePlaceholderPrefix)
	post := []byte("HBHB")
	pStart := []byte("<p>")
	pEnd := []byte("</p>")

	k := bytes.Index(source[start:], pre)

	for k != -1 {
		j := start + k
		postIdx := bytes.Index(source[j:], post)
		if postIdx < 0 {
			// this should never happen, but let the caller decide to panic or not
			return nil, errors.New("illegal state in content; shortcode token missing end delim")
		}

		end := j + postIdx + 4
		key := string(source[j:end])
		newVal, err := tokenHandler(ctx, key)
		if err != nil {
			return nil, err
		}

		// Issue #1148: Check for wrapping p-tags <p>
		if j >= 3 && bytes.Equal(source[j-3:j], pStart) {
			if (k+4) < len(source) && bytes.Equal(source[end:end+4], pEnd) {
				j -= 3
				end += 4
			}
		}

		// This and other cool slice tricks: https://github.com/golang/go/wiki/SliceTricks

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Identify which page fails and simplify: temporarily remove shortcodes one at a time to find the placement (e.g. inside a code block, link title, or attribute) whose rendering mangles the token.
  2. Check custom render hooks/markup filters that rewrite rendered text and exclude Hugo's shortcode placeholder tokens from transformation.
  3. Update Hugo to the latest release, and if it still reproduces with a minimal case, report it as a bug — this state indicates an internal invariant break.
Defensive patterns

Strategy: validation

Validate before calling

// Detect unterminated shortcode delimiters in content
// grep -nE '\{\{[<%]([^}]|\}[^}])*$' content/**/*.md

Try / catch

if err := h.Build(cfg); err != nil {
    // 'missing end delim' means a {{< or {{% was never closed with >}} / %}}
    // find the last shortcode open in the named file
    return err
}

Prevention

When it happens

Trigger: expandShortcodeTokens finding the shortcode placeholder prefix in rendered content without a following 'HBHB' marker. This occurs when the markdown renderer or a filter mutates, splits, or truncates the placeholder string — e.g. a render hook, custom Goldmark extension, or a template transformation altering the token text.

Common situations: Custom markdown render hooks or output filters that transform text containing the placeholder; shortcodes placed in contexts where the renderer escapes or breaks the token (e.g. inside code processing or transformed attributes); very rarely a Hugo bug after version upgrades — historically reported around unusual shortcode placement.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/658acd794984031f.json. Report an issue: GitHub ↗.