siyuan-note/siyuan · error

invalid video poster: %w

Error message

invalid video poster: %w

What it means

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".

Source

Thrown at kernel/model/boot_appearance.go:473

		if layer == nil || !isValidBootAppearanceID(layer.ID) || layerIDs[layer.ID] {
			err = errors.New("invalid or duplicate layer ID")
			return nil, err
		}
		layerIDs[layer.ID] = true
		if layer.Type != "image" && layer.Type != "video" {
			err = fmt.Errorf("unsupported layer type [%s]", layer.Type)
			return nil, err
		}
		if _, _, err = validateBootAppearanceResource(pluginDir, appearanceDir, layer.Src, layer.Type); err != nil {
			return nil, fmt.Errorf("invalid layer source: %w", err)
		}
		if layer.Type == "video" {
			if layer.Poster == "" {
				err = errors.New("video poster is required")
				return nil, err
			}
			if _, _, err = validateBootAppearanceResource(pluginDir, appearanceDir, layer.Poster, "image"); err != nil {
				return nil, fmt.Errorf("invalid video poster: %w", err)
			}
		} else if layer.Poster != "" {
			err = errors.New("image layer cannot declare a poster")
			return nil, err
		}
		fit := layer.Fit
		if fit == "" {
			fit = "cover"
		}
		if !isValidBootAppearanceFit(fit) {
			err = fmt.Errorf("invalid layer fit [%s]", fit)
			return nil, err
		}
		position := layer.Position
		if position == "" {
			position = "center"
		}
		if !isValidBootAppearancePosition(position) {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Ensure the poster file exists in the appearance directory and the path is a relative '/'-separated path
  2. Use .png, .jpg, .jpeg, or .webp and confirm the file really is that image format (convert GIFs to WebP/PNG)
  3. Compress the poster under maxBootAppearanceImageSize
  4. Remove symlinks and place the real image file in the package

Example fix

// before
{"id": "anim", "type": "video", "src": "anim.mp4", "poster": "anim.gif"}
// after
{"id": "anim", "type": "video", "src": "anim.mp4", "poster": "anim.webp"}  // real WebP file present
Defensive patterns

Strategy: validation

Validate before calling

manifest.layers?.forEach(l => {
  if (l.type !== "video" || !l.poster) return;
  if (!/\.(png|jpe?g|webp)$/i.test(l.poster)) throw new Error(`poster ${l.poster} must be png/jpg/webp`);
  const p = join(appearanceDir, l.poster);
  if (!existsSync(p)) throw new Error(`poster missing: ${l.poster}`);
});

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/ad139e6b3bd454e2. Report an issue: GitHub.