gohugoio/hugo · error
%s: shortcode %q must be closed or self-closed
Error message
%s: shortcode %q must be closed or self-closed
What it means
The parser reached the end of the page content while inside a shortcode whose template uses .Inner/.InnerDeindent (needsInner) but never saw its closing tag or a self-closing '/>'. Because inner-consuming shortcodes delimit a content region, an unterminated one is ambiguous, so Hugo aborts instead of guessing where it ends.
Source
Thrown at hugolib/shortcode.go:711
} else {
// positional params
if sc.params == nil {
var params []any
params = append(params, currItem.ValTyped(source))
sc.params = params
} else {
if params, ok := sc.params.([]any); ok {
params = append(params, currItem.ValTyped(source))
sc.params = params
} else {
return sc, fmt.Errorf("%s: invalid state: invalid param type %T for shortcode %q, expected a slice", errorPrefix, params, sc.name)
}
}
}
case currItem.IsDone():
if !currItem.IsError() {
if !closed && sc.needsInner() {
return sc, fmt.Errorf("%s: shortcode %q must be closed or self-closed", errorPrefix, sc.name)
}
}
// handled by caller
pt.Backup()
break Loop
}
}
return sc, nil
}
// Replace prefixed shortcode tokens with the real content.
// Note: This function will rewrite the input slice.
func expandShortcodeTokens(
ctx context.Context,
source []byte,
tokenHandler func(ctx context.Context, token string) ([]byte, error),
) ([]byte, error) {View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Add the matching closing tag {{< /name >}} (or {{% /name %}} for the markup form) at the intended end of the inner content.
- If no inner content is wanted, self-close it: {{< name />}}.
- Check for delimiter mismatch — the closing tag must use the same delimiter style ({{< >}} vs {{% %}}) as the opener, and nested shortcodes must close in order.
Example fix
<!-- before -->
{{< details summary="More" >}}
Hidden text
<!-- after -->
{{< details summary="More" >}}
Hidden text
{{< /details >}} Defensive patterns
Strategy: validation
Validate before calling
// Lint: every {{< name >}} whose template uses .Inner must have a matching {{< /name >}}
// or be written self-closed {{< name />}} Prevention
- Always close inner-consuming shortcodes: {{< name >}}...{{< /name >}}
- Check closing tag name matches the opening tag exactly — mismatched names read as unclosed
- Don't nest markdown headings or other shortcodes in ways that swallow the closing tag
- Use the reported file:line position in the error to jump straight to the unclosed call
When it happens
Trigger: Content contains {{< name >}} for a shortcode whose template references .Inner, but the matching {{< /name >}} is missing, misspelled (e.g. {{< /nmae >}}), malformed, or the call wasn't self-closed as {{< name />}}. Nested shortcodes with mismatched closers trigger it too.
Common situations: Forgetting the closing tag after switching a shortcode to use .Inner; truncated content files; mismatched delimiters ({{% name %}} opened but {{< /name >}} used to close); copy-paste dropping the closing line; nesting errors where an inner shortcode consumes the outer's closer.
Related errors
- shortcode has no name
- error processing file %q
- invalid Highlight option: %s
- no earlier definition of shortcode %q found
- %s: shortcode %q does not evaluate .Inner or .InnerDeindent,
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/d1875dac9d7c1f94.json.
Report an issue: GitHub ↗.