{"record":{"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":"validation","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/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/hugolib/shortcode.go#L693-L729","documentation":"A shortcode that expects inner content (its template evaluates .Inner or .InnerDeindent, i.e. needsInner() is true) was opened but never closed, nor was it self-closed. Hugo requires such shortcodes to appear as a matched pair {{< name >}}...{{< /name >}} or as a self-closing tag {{< name />}}.","triggerScenarios":"Writing {{< highlight go >}} without the corresponding {{< /highlight >}}, or forgetting the trailing slash on a self-closing form. The check at shortcode.go:710 fires when parsing reaches the IsDone state while closed==false and sc.needsInner()==true.","commonSituations":"Closing a code block with ``` but forgetting {{< /highlight >}}; renaming a shortcode and leaving an orphan opening tag; content pasted from a Markdown source that stripped the closing tag.","solutions":["Add the matching closing tag {{< /shortcodeName >}} after the inner content.","If the shortcode should not wrap content, use the self-closing form {{< shortcodeName />}}.","Verify the shortcode template truly needs inner content; if it does not use .Inner, the opening/closing pairing is unnecessary.","Search the reported content file for the shortcode name to locate the unbalanced tag."],"exampleFix":"// before\n{{< highlight go >}}\nfunc main() {}\n\n// after\n{{< highlight go >}}\nfunc main() {}\n{{< /highlight >}}","handlingStrategy":"validation","validationCode":"// Lint content for unclosed wrapping shortcodes by pairing open/close tokens.\nfunc validateShortcodeBalance(tokens []scToken) error {\n    var stack []string\n    for _, t := range tokens {\n        switch {\n        case t.isOpen():\n            if !t.isSelfClosing() { stack = append(stack, t.name) }\n        case t.isClose():\n            if len(stack) == 0 || stack[len(stack)-1] != t.name {\n                return fmt.Errorf(\"unbalanced closing tag %q\", t.name)\n            }\n            stack = stack[:len(stack)-1]\n        }\n    }\n    if len(stack) > 0 {\n        return fmt.Errorf(\"shortcode %q must be closed or self-closed\", stack[len(stack)-1])\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always close wrapping shortcodes; prefer self-closing form when no inner content is needed.","Use an editor/CI linter that checks shortcode tag balance.","Keep a short list of shortcodes that require closing tags next to your content guide."],"tags":["shortcode","template","syntax","hugo"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}