{"id":"d1875dac9d7c1f94","repo":"gohugoio/hugo","slug":"s-shortcode-q-must-be-closed-or-self-closed","errorCode":null,"errorMessage":"%s: shortcode %q must be closed or self-closed","messagePattern":"(.+?): shortcode %q must be closed or self-closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hugolib/shortcode.go","lineNumber":711,"sourceCode":"\t\t\t} else {\n\t\t\t\t// positional params\n\t\t\t\tif sc.params == nil {\n\t\t\t\t\tvar params []any\n\t\t\t\t\tparams = append(params, currItem.ValTyped(source))\n\t\t\t\t\tsc.params = params\n\t\t\t\t} else {\n\t\t\t\t\tif params, ok := sc.params.([]any); ok {\n\t\t\t\t\t\tparams = append(params, currItem.ValTyped(source))\n\t\t\t\t\t\tsc.params = params\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn sc, fmt.Errorf(\"%s: invalid state: invalid param type %T for shortcode %q, expected a slice\", errorPrefix, params, sc.name)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase currItem.IsDone():\n\t\t\tif !currItem.IsError() {\n\t\t\t\tif !closed && sc.needsInner() {\n\t\t\t\t\treturn sc, fmt.Errorf(\"%s: shortcode %q must be closed or self-closed\", errorPrefix, sc.name)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// handled by caller\n\t\t\tpt.Backup()\n\t\t\tbreak Loop\n\n\t\t}\n\t}\n\treturn sc, nil\n}\n\n// Replace prefixed shortcode tokens with the real content.\n// Note: This function will rewrite the input slice.\nfunc expandShortcodeTokens(\n\tctx context.Context,\n\tsource []byte,\n\ttokenHandler func(ctx context.Context, token string) ([]byte, error),\n) ([]byte, error) {","sourceCodeStart":693,"sourceCodeEnd":729,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/hugolib/shortcode.go#L693-L729","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"<!-- before -->\n{{< details summary=\"More\" >}}\nHidden text\n\n<!-- after -->\n{{< details summary=\"More\" >}}\nHidden text\n{{< /details >}}","handlingStrategy":"validation","validationCode":"// Lint: every {{< name >}} whose template uses .Inner must have a matching {{< /name >}}\n// or be written self-closed {{< name />}}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["hugo","shortcode","content","parsing"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}