larksuite/cli · error

expected <%s>, got <%s>

Error message

expected <%s>, got <%s>

What it means

parseHTML5BlockStartTag parses a start-tag fragment with the XML tokenizer and requires the first element to be the expected html5 block tag (html5BlockTag, e.g. a specific container tag used by doc resource rewriting). When the fragment's root element has a different local name, it reports 'expected <X>, got <Y>'. Callers wrap this into typed validation errors.

Source

Thrown at shortcuts/doc/html5_block_resources.go:887

func parseHTML5BlockStartTag(raw string) (html5BlockStartTag, error) {
	trimmed := strings.TrimSpace(raw)
	selfClosing := strings.HasSuffix(trimmed, "/>")
	decoder := xml.NewDecoder(strings.NewReader(raw))
	for {
		tok, err := decoder.Token()
		if err != nil {
			if errors.Is(err, io.EOF) {
				break
			}
			return html5BlockStartTag{}, err
		}
		start, ok := tok.(xml.StartElement)
		if !ok {
			continue
		}
		if start.Name.Local != html5BlockTag {
			return html5BlockStartTag{}, fmt.Errorf("expected <%s>, got <%s>", html5BlockTag, start.Name.Local) //nolint:forbidigo // intermediate parse helper; callers wrap with typed validation errors.
		}
		attrs := make([]html5BlockAttr, 0, len(start.Attr))
		for _, attr := range start.Attr {
			attrs = append(attrs, html5BlockAttr{Name: attr.Name.Local, Value: attr.Value})
		}
		return html5BlockStartTag{Attrs: attrs, SelfClosing: selfClosing}, nil
	}
	return html5BlockStartTag{}, fmt.Errorf("missing start element") //nolint:forbidigo // intermediate parse helper; callers wrap with typed validation errors.
}

func parseWhiteboardStartTag(raw string) (whiteboardStartTag, error) {
	trimmed := strings.TrimSpace(raw)
	selfClosing := strings.HasSuffix(trimmed, "/>")
	decoder := xml.NewDecoder(strings.NewReader(raw))
	for {
		tok, err := decoder.Token()
		if err != nil {
			if errors.Is(err, io.EOF) {

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Inspect the failing fragment (the wrapped validation error includes the raw markup) and see which tag was actually present.
  2. Ensure the content is normalized/converted to the expected doc HTML structure before the rewrite step (e.g. via the doc format converter).
  3. If this is your pipeline feeding the command, sanitize or map nonstandard tags to the expected block tag before invoking.
Defensive patterns

Strategy: validation

Validate before calling

// Peek at the first start element before invoking the rewrite:
dec := xml.NewDecoder(strings.NewReader(fragment))
for {
    tok, err := dec.Token()
    if err == io.EOF { log.Fatal("no element found") }
    if err != nil { log.Fatal(err) }
    if se, ok := tok.(xml.StartElement); ok {
        if se.Name.Local != expectedTag { log.Fatalf("root is <%s>, want <%s>", se.Name.Local, expectedTag) }
        break
    }
}

Type guard

func rootElementIs(fragment, want string) bool {
    dec := xml.NewDecoder(strings.NewReader(fragment))
    for {
        tok, err := dec.Token()
        if err != nil { return false }
        if se, ok := tok.(xml.StartElement); ok { return se.Name.Local == want }
    }
}

Try / catch

// Wrap the helper error into the typed validation error the callers use:
out, err := parseHTML5BlockStartTag(raw)
if err != nil {
    return errs.NewValidationError(errs.SubtypeInvalidArgument, "invalid html5 block %q: %s", raw, err).WithCause(err)
}

Prevention

When it happens

Trigger: Doc content rewriting that extracts an HTML5 block start tag receives markup whose first element is a different tag — e.g. nested/renamed container tags, custom tags in the document, or markup that begins with a child element instead of the expected block wrapper.

Common situations: Feishu/Lark doc content with unexpected or legacy HTML structures, third-party pasted content with nonstandard tags, or template/normalization changes upstream producing a different root element.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/b446b556875b2161. Report an issue: GitHub.