{"record":{"id":"c01d8dd92f2094c3","repo":"siyuan-note/siyuan","slug":"name-must-end-in-html-or-htm","errorCode":null,"errorMessage":"name must end in .html or .htm","messagePattern":"name must end in \\.html or \\.htm","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/tools/asset.go","lineNumber":176,"sourceCode":"\t\tContent: []ContentItem{{Type: \"text\", Text: fmt.Sprintf(\"Created HTML IFrame block: %s\\nAsset: %s\", blockID, assetPath)}},\n\t\tStructuredContent: map[string]any{\n\t\t\t\"blockID\":   blockID,\n\t\t\t\"assetPath\": assetPath,\n\t\t},\n\t\tStructuredContentSet: true,\n\t}, nil\n}\n\nfunc normalizeHTMLAssetName(value any) (string, error) {\n\tname, _ := value.(string)\n\tname = strings.TrimSpace(name)\n\tif name == \"\" {\n\t\tname = \"component.html\"\n\t}\n\tname = filepath.Base(name)\n\text := strings.ToLower(filepath.Ext(name))\n\tif ext != \".html\" && ext != \".htm\" {\n\t\treturn \"\", fmt.Errorf(\"name must end in .html or .htm\")\n\t}\n\treturn name, nil\n}\n\nfunc htmlAssetIFrameBlockDOM(assetPath string) (dom, blockID string, err error) {\n\tsrc := html.EscapeString(model.HTMLAssetIFrameSrc(assetPath))\n\tdom, err = markdownToBlockDOM(`<iframe sandbox=\"allow-scripts\" src=\"` + src + `\" border=\"0\" frameborder=\"no\" framespacing=\"0\" allowfullscreen=\"true\"></iframe>`)\n\tif err != nil {\n\t\treturn\n\t}\n\ttree := util.NewLute().BlockDOM2Tree(dom)\n\tif tree == nil || tree.Root == nil || tree.Root.FirstChild == nil {\n\t\treturn \"\", \"\", fmt.Errorf(\"empty IFrame block\")\n\t}\n\tblockID = tree.Root.FirstChild.ID\n\treturn\n}\n","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/tools/asset.go#L158-L194","documentation":"normalizeHTMLAssetName validates that the requested asset filename has an .html or .htm extension. The name is reduced to its base path and its lowercase extension is checked; anything else is rejected so only HTML components can be created by assetCreateHTML.","triggerScenarios":"assetCreateHTML is called with a name argument that is empty-derived, a bare filename without extension, or ending in another extension (.txt, .HTML is fine after lowering, .md is rejected).","commonSituations":"Callers passing a title/label instead of a filename; omitting the extension assuming the tool appends one; passing a path whose base name lacks the extension (extension in a directory part).","solutions":["Append a .html or .htm extension to the name argument before calling the tool","Use the default by passing an empty name (the tool substitutes \"component.html\") if no specific name is needed","Strip any directory prefix yourself — only the base name's extension matters"],"exampleFix":"// before\nassetCreateHTML(name: \"my-widget\")\n// after\nassetCreateHTML(name: \"my-widget.html\")","handlingStrategy":"validation","validationCode":"func validHTMLAssetName(name string) bool {\n    ext := strings.ToLower(filepath.Ext(filepath.Base(name)))\n    return ext == \".html\" || ext == \".htm\"\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass a filename with .html/.htm extension, or pass empty name for the default","Do not rely on the tool to append extensions","Remember only the base name's extension is checked — directories are stripped"],"tags":["validation","filename","mcp"],"backgroundTag":"invalid-argument-format","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}