{"record":{"id":"ad139e6b3bd454e2","repo":"siyuan-note/siyuan","slug":"invalid-video-poster-w","errorCode":null,"errorMessage":"invalid video poster: %w","messagePattern":"invalid video poster: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/boot_appearance.go","lineNumber":473,"sourceCode":"\t\tif layer == nil || !isValidBootAppearanceID(layer.ID) || layerIDs[layer.ID] {\n\t\t\terr = errors.New(\"invalid or duplicate layer ID\")\n\t\t\treturn nil, err\n\t\t}\n\t\tlayerIDs[layer.ID] = true\n\t\tif layer.Type != \"image\" && layer.Type != \"video\" {\n\t\t\terr = fmt.Errorf(\"unsupported layer type [%s]\", layer.Type)\n\t\t\treturn nil, err\n\t\t}\n\t\tif _, _, err = validateBootAppearanceResource(pluginDir, appearanceDir, layer.Src, layer.Type); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid layer source: %w\", err)\n\t\t}\n\t\tif layer.Type == \"video\" {\n\t\t\tif layer.Poster == \"\" {\n\t\t\t\terr = errors.New(\"video poster is required\")\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif _, _, err = validateBootAppearanceResource(pluginDir, appearanceDir, layer.Poster, \"image\"); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid video poster: %w\", err)\n\t\t\t}\n\t\t} else if layer.Poster != \"\" {\n\t\t\terr = errors.New(\"image layer cannot declare a poster\")\n\t\t\treturn nil, err\n\t\t}\n\t\tfit := layer.Fit\n\t\tif fit == \"\" {\n\t\t\tfit = \"cover\"\n\t\t}\n\t\tif !isValidBootAppearanceFit(fit) {\n\t\t\terr = fmt.Errorf(\"invalid layer fit [%s]\", fit)\n\t\t\treturn nil, err\n\t\t}\n\t\tposition := layer.Position\n\t\tif position == \"\" {\n\t\t\tposition = \"center\"\n\t\t}\n\t\tif !isValidBootAppearancePosition(position) {","sourceCodeStart":455,"sourceCodeEnd":491,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/boot_appearance.go#L455-L491","documentation":"A video layer's poster must itself pass validateBootAppearanceResource with expectedType \"image\": relative safe path, existing regular file, .png/.jpg/.jpeg/.webp extension, image size cap, and sniffed content must really be an image. Failures are wrapped as \"invalid video poster\".","triggerScenarios":"GetBootAppearances/getBootAppearanceByID loading a manifest where a video layer's poster points to a missing file, a non-image extension (e.g. .mp4, .gif), a traversal/symlink path, or a file whose bytes are not actually a PNG/JPEG/WebP image.","commonSituations":"Poster GIF not accepted (rename/convert to webp/png); poster.png forgotten when copying the appearance folder to another machine; poster extension renamed from .jpg to .png without converting; poster file larger than the image size cap.","solutions":["Ensure the poster file exists in the appearance directory and the path is a relative '/'-separated path","Use .png, .jpg, .jpeg, or .webp and confirm the file really is that image format (convert GIFs to WebP/PNG)","Compress the poster under maxBootAppearanceImageSize","Remove symlinks and place the real image file in the package"],"exampleFix":"// before\n{\"id\": \"anim\", \"type\": \"video\", \"src\": \"anim.mp4\", \"poster\": \"anim.gif\"}\n// after\n{\"id\": \"anim\", \"type\": \"video\", \"src\": \"anim.mp4\", \"poster\": \"anim.webp\"}  // real WebP file present","handlingStrategy":"validation","validationCode":"manifest.layers?.forEach(l => {\n  if (l.type !== \"video\" || !l.poster) return;\n  if (!/\\.(png|jpe?g|webp)$/i.test(l.poster)) throw new Error(`poster ${l.poster} must be png/jpg/webp`);\n  const p = join(appearanceDir, l.poster);\n  if (!existsSync(p)) throw new Error(`poster missing: ${l.poster}`);\n});","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Convert posters to PNG/JPEG/WebP (never GIF) and verify the format with an image tool","Ship the poster file with the plugin; do not reference files outside the appearance dir","Keep posters under the image size cap","Verify the poster renders (e.g. open in browser) before publishing"],"tags":["validation","video","image","plugin"],"backgroundTag":"resource-not-found","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}