siyuan-note/siyuan · error

name must end in .html or .htm

Error message

name must end in .html or .htm

What it means

`normalizeHTMLAssetName` requires the supplied asset file name to end in `.html` or `.htm` (case-insensitive). The HTML asset feature stores a single HTML file that gets embedded via an `<iframe>`, so only HTML extensions are accepted; any other extension is rejected before the file is written.

Source

Thrown at kernel/mcp/tools/asset.go:176

		Content: []ContentItem{{Type: "text", Text: fmt.Sprintf("Created HTML IFrame block: %s\nAsset: %s", blockID, assetPath)}},
		StructuredContent: map[string]any{
			"blockID":   blockID,
			"assetPath": assetPath,
		},
		StructuredContentSet: true,
	}, nil
}

func normalizeHTMLAssetName(value any) (string, error) {
	name, _ := value.(string)
	name = strings.TrimSpace(name)
	if name == "" {
		name = "component.html"
	}
	name = filepath.Base(name)
	ext := strings.ToLower(filepath.Ext(name))
	if ext != ".html" && ext != ".htm" {
		return "", fmt.Errorf("name must end in .html or .htm")
	}
	return name, nil
}

func htmlAssetIFrameBlockDOM(assetPath string) (dom, blockID string, err error) {
	src := html.EscapeString(model.HTMLAssetIFrameSrc(assetPath))
	dom, err = markdownToBlockDOM(`<iframe sandbox="allow-scripts" src="` + src + `" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>`)
	if err != nil {
		return
	}
	tree := util.NewLute().BlockDOM2Tree(dom)
	if tree == nil || tree.Root == nil || tree.Root.FirstChild == nil {
		return "", "", fmt.Errorf("empty IFrame block")
	}
	blockID = tree.Root.FirstChild.ID
	return
}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Supply a `name` ending in `.html` (or `.htm`), e.g. `component.html`.
  2. If `name` is omitted it defaults to `component.html`; only override it with another `.html`/`.htm` value.
  3. Remember only `filepath.Base` is kept, so directories in the value are ignored — set the extension on the filename itself.

Example fix

// before
{"name": "widget.js", "content": "..."}
// after
{"name": "widget.html", "content": "..."}
Defensive patterns

Strategy: validation

Validate before calling

// Validate the asset name extension before calling the tool.
func validHTMLName(name string) bool {
    ext := strings.ToLower(filepath.Ext(strings.TrimSpace(name)))
    return ext == ".html" || ext == ".htm"
}

Prevention

When it happens

Trigger: Calling the HTML-asset MCP tool with a `name` argument whose extension (after `filepath.Base` and lowercasing) is neither `.html` nor `.htm` — e.g. `.txt`, `.svg`, `.js`, or no extension at all.

Common situations: Passing a full path whose final component has a different extension. Providing a component name like `widget.js` when an HTML wrapper is required. Forgetting the extension entirely.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/c01d8dd92f2094c3. Report an issue: GitHub.