wavetermdev/waveterm · error
bind tags must be self closing
Error message
bind tags must be self closing
What it means
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.
Source
Thrown at pkg/vdom/vdom_html.go:335
elemPath = elemPath[:len(elemPath)-1]
}
}
func Bind(htmlStr string, params map[string]any) *VDomElem {
htmlStr = processWhitespace(htmlStr)
r := strings.NewReader(htmlStr)
iter := htmltoken.NewTokenizer(r)
var elemStack []*VDomElem
elemStack = append(elemStack, &VDomElem{Tag: FragmentTag})
var tokenErr error
outer:
for {
tokenType := iter.Next()
token := iter.Token()
switch tokenType {
case htmltoken.StartTagToken:
if token.Data == Html_BindTagName || token.Data == Html_BindParamTagName {
tokenErr = errors.New("bind tags must be self closing")
break outer
}
elem := tokenToElem(token, params)
elemStack = pushElemStack(elemStack, elem)
case htmltoken.EndTagToken:
if token.Data == Html_BindTagName || token.Data == Html_BindParamTagName {
tokenErr = errors.New("bind tags must be self closing")
break outer
}
if len(elemStack) <= 1 {
tokenErr = fmt.Errorf("end tag %q without start tag", token.Data)
break outer
}
if curElemTag(elemStack) != token.Data {
tokenErr = fmt.Errorf("end tag %q does not match start tag %q", token.Data, curElemTag(elemStack))
break outer
}
elemStack = popElemStack(elemStack)View on GitHub (pinned to a4447c1563)
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.
Example fix
// before <div><bind key="header"><span>fallback</span></bind></div> // after <div><bind key="header" /><span>fallback</span></div>
Defensive patterns
Strategy: validation
Validate before calling
// pre-validate template text before vdom.Bind
if strings.Contains(html, "<bind") || strings.Contains(html, "<bind-param") {
re := regexp.MustCompile(`<(bind|bind-param)\b[^>]*[^/]>`)
if m := re.FindString(html); m != "" {
return fmt.Errorf("bind tag not self-closing: %s", m)
}
} Try / catch
elem, err := vdom.Bind(html, params)
if err != nil && strings.Contains(err.Error(), "bind tags must be self closing") {
return fmt.Errorf("template error: use <bind ... /> not <bind ...>...</bind>: %w", err)
}
if err != nil {
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- doctype not supported
- failed to prepare install command: %w
- TermRef not current
- beginning of file
- procinfo: process not found
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/c2a68de328f190ee.
Report an issue: GitHub.