{"record":{"id":"edaa956d9e614d17","repo":"siyuan-note/siyuan","slug":"empty-iframe-block","errorCode":null,"errorMessage":"empty IFrame block","messagePattern":"empty IFrame block","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/tools/asset.go","lineNumber":189,"sourceCode":"\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\nfunc newAssetUploadToolResult(succeeded []model.AssetUploadSuccess, failed []model.AssetUploadFailure) CallToolResult {\n\tvar sb strings.Builder\n\tsb.WriteString(fmt.Sprintf(\"Uploaded %d file(s):\\n\", len(succeeded)))\n\tfor _, result := range succeeded {\n\t\tsb.WriteString(fmt.Sprintf(\"- %s -> %s\\n\", result.Name, result.Path))\n\t}\n\tif 0 < len(failed) {\n\t\tsb.WriteString(fmt.Sprintf(\"\\nFailed %d file(s):\\n\", len(failed)))\n\t\tfor _, result := range failed {\n\t\t\tsb.WriteString(fmt.Sprintf(\"- %s: %s\\n\", result.Name, result.Error))\n\t\t}\n\t}\n\treturn CallToolResult{Content: []ContentItem{{Type: \"text\", Text: sb.String()}}, IsError: 0 < len(failed)}","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/mcp/tools/asset.go#L171-L207","documentation":"htmlAssetIFrameBlockDOM converts a generated iframe markdown snippet into a block DOM tree via Lute and expects the tree to contain a root with a first child block. If Lute produced an empty tree (nil root or no first child), parsing failed silently and this error reports it instead of returning a bogus block ID.","triggerScenarios":"markdownToBlockDOM rendered the iframe snippet into an empty/invalid DOM — e.g. Lute configuration stripped the raw HTML iframe, markdownToBlockDOM returned \"\", or BlockDOM2Tree failed to parse the produced DOM string.","commonSituations":"A Lute version change altering raw-HTML/iframe parsing; markdownToBlockDOM returning empty output due to an upstream error that wasn't propagated; unusual sanitizer settings filtering iframe tags.","solutions":["Check whether markdownToBlockDOM returned an empty string or error and propagate it before calling BlockDOM2Tree","Verify the Lute version parses raw HTML iframe blocks (test with util.NewLute().BlockDOM2Tree on a minimal iframe snippet)","If a sanitizer/filter strips iframe, adjust the parsing options so the iframe markdown is preserved"],"exampleFix":"// before\ndom, err = markdownToBlockDOM(iframeMD)\nif err != nil { return } // dom may be empty, error surfaces later\n// after\ndom, err = markdownToBlockDOM(iframeMD)\nif err != nil { return }\nif dom == \"\" { return \"\", \"\", fmt.Errorf(\"empty DOM for iframe block\") }","handlingStrategy":"validation","validationCode":"tree := util.NewLute().BlockDOM2Tree(dom)\nif tree == nil || tree.Root == nil || tree.Root.FirstChild == nil { return errors.New(\"parsed DOM is empty\") }","typeGuard":"func hasFirstChildBlock(tree *parse.Tree) bool { return tree != nil && tree.Root != nil && tree.Root.FirstChild != nil }","tryCatchPattern":"blockID, dom, err := htmlAssetIFrameBlockDOM(assetPath)\nif err != nil && strings.Contains(err.Error(), \"empty IFrame block\") {\n    // re-render the DOM or upgrade/inspect the Lute parsing path\n}","preventionTips":["Check markdownToBlockDOM output for empty strings before tree conversion","Keep Lute updated and add a regression test parsing an iframe snippet","Avoid sanitizer settings that strip raw HTML iframe tags"],"tags":["mcp","parsing","lute"],"backgroundTag":"empty-result-set","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}