{"record":{"id":"c2a68de328f190ee","repo":"wavetermdev/waveterm","slug":"bind-tags-must-be-self-closing","errorCode":null,"errorMessage":"bind tags must be self closing","messagePattern":"bind tags must be self closing","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/vdom/vdom_html.go","lineNumber":335,"sourceCode":"\t\telemPath = elemPath[:len(elemPath)-1]\n\t}\n}\n\nfunc Bind(htmlStr string, params map[string]any) *VDomElem {\n\thtmlStr = processWhitespace(htmlStr)\n\tr := strings.NewReader(htmlStr)\n\titer := htmltoken.NewTokenizer(r)\n\tvar elemStack []*VDomElem\n\telemStack = append(elemStack, &VDomElem{Tag: FragmentTag})\n\tvar tokenErr error\nouter:\n\tfor {\n\t\ttokenType := iter.Next()\n\t\ttoken := iter.Token()\n\t\tswitch tokenType {\n\t\tcase htmltoken.StartTagToken:\n\t\t\tif token.Data == Html_BindTagName || token.Data == Html_BindParamTagName {\n\t\t\t\ttokenErr = errors.New(\"bind tags must be self closing\")\n\t\t\t\tbreak outer\n\t\t\t}\n\t\t\telem := tokenToElem(token, params)\n\t\t\telemStack = pushElemStack(elemStack, elem)\n\t\tcase htmltoken.EndTagToken:\n\t\t\tif token.Data == Html_BindTagName || token.Data == Html_BindParamTagName {\n\t\t\t\ttokenErr = errors.New(\"bind tags must be self closing\")\n\t\t\t\tbreak outer\n\t\t\t}\n\t\t\tif len(elemStack) <= 1 {\n\t\t\t\ttokenErr = fmt.Errorf(\"end tag %q without start tag\", token.Data)\n\t\t\t\tbreak outer\n\t\t\t}\n\t\t\tif curElemTag(elemStack) != token.Data {\n\t\t\t\ttokenErr = fmt.Errorf(\"end tag %q does not match start tag %q\", token.Data, curElemTag(elemStack))\n\t\t\t\tbreak outer\n\t\t\t}\n\t\t\telemStack = popElemStack(elemStack)","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/vdom/vdom_html.go#L317-L353","documentation":"During HTML parsing in vdom.Bind, a start tag using the reserved bind tag names (<bind> or <bind-param>) was found that was not self-closing. Bind tags are placeholders substituted by the framework and must be written as <bind .../> — an opening <bind> tag breaks the parse and this error is raised immediately. Note the error is created fresh (errors.New) inside the token loop rather than a package sentinel.","triggerScenarios":"Writing <bind ...> (with children or a plain closing tag) in HTML handed to vdom.Bind instead of the self-closing form <bind .../>; same for <bind-param> start tags.","commonSituations":"Porting templates written in JSX/Vue style where component tags wrap children; hand-editing HTML and forgetting the trailing slash; copy-pasting examples from non-VDOM templates.","solutions":["Rewrite the bind tag as self-closing: <bind name=\"...\" /> instead of <bind name=\"...\">.</bind>.","Remove any children inside the bind tag — bind tags are leaf placeholders; move child content outside the tag.","Run the template through vdom.Bind early in development/tests to catch malformed bind syntax before runtime."],"exampleFix":"// before\n<div><bind key=\"header\"><span>fallback</span></bind></div>\n// after\n<div><bind key=\"header\" /><span>fallback</span></div>","handlingStrategy":"validation","validationCode":"// pre-validate template text before vdom.Bind\nif strings.Contains(html, \"<bind\") || strings.Contains(html, \"<bind-param\") {\n    re := regexp.MustCompile(`<(bind|bind-param)\\b[^>]*[^/]>`)\n    if m := re.FindString(html); m != \"\" {\n        return fmt.Errorf(\"bind tag not self-closing: %s\", m)\n    }\n}","typeGuard":null,"tryCatchPattern":"elem, err := vdom.Bind(html, params)\nif err != nil && strings.Contains(err.Error(), \"bind tags must be self closing\") {\n    return fmt.Errorf(\"template error: use <bind ... /> not <bind ...>...</bind>: %w\", err)\n}\nif err != nil {\n    return err\n}","preventionTips":["Always write bind tags self-closing: <bind ... /> and <bind-param ... />.","Never put child content inside bind tags; they are leaf placeholders.","Disable editor auto-closing for these custom tag names.","Add a template-parsing unit test that runs all shipped templates through vdom.Bind."],"tags":["go","html","template","vdom","parse-error"],"backgroundTag":"invalid-template-syntax","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}