siyuan-note/siyuan · error

invalid appearance ID

Error message

invalid appearance ID

What it means

loadBootAppearance rejects the appearance ID before touching disk because it fails isValidBootAppearanceID: it must match the boot appearance ID pattern and be at most 64 characters. The kernel only loads boot appearances whose directory/manifest ID conform to this safe-identifier format, which prevents path traversal and filesystem-incompatible names.

Source

Thrown at kernel/model/boot_appearance.go:377

	if err != nil {
		return
	}
	current := defaultBootAppearanceSelection()
	if err = gulu.JSON.UnmarshalJSON(data, &current); err != nil || current != expected {
		return
	}
	data, err = gulu.JSON.MarshalIndentJSON(defaultBootAppearanceSelection(), "", "\t")
	if err != nil {
		return
	}
	if err = filelock.WriteFile(configPath, data); err != nil {
		logging.LogWarnf("clear invalid boot appearance selection failed: %s", err)
	}
}

func loadBootAppearance(pluginDir string, pkg *bazaar.Package, appearanceID string) (ret *BootAppearance, err error) {
	if !isValidBootAppearanceID(appearanceID) {
		err = errors.New("invalid appearance ID")
		return
	}
	appearanceDir := filepath.Join(pluginDir, bootAppearanceDirName, appearanceID)
	if err = validateBootAppearancePackage(pluginDir, appearanceDir); err != nil {
		return
	}
	manifestPath, _, resolveErr := validateBootAppearanceResource(pluginDir, appearanceDir, bootAppearanceManifestName, "manifest")
	if resolveErr != nil {
		err = resolveErr
		return
	}
	data, readErr := filelock.ReadFile(manifestPath)
	if readErr != nil {
		err = readErr
		return
	}
	manifest := &bootAppearanceManifest{}
	if err = gulu.JSON.UnmarshalJSON(data, manifest); err != nil {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Rename the appearance directory under data/plugins/<provider>/appearances/ (or the plugin's boot appearance folder) so the ID matches the allowed pattern and is <= 64 characters, e.g. 'my-boot-screen'
  2. Update the appearance's manifest.json so its ID field exactly matches the renamed directory name
  3. If a bad selection was persisted, delete or reset the boot appearance selection config so the kernel falls back to the default boot appearance
  4. Update the plugin's plugin.json bootAppearances list to reference the corrected ID

Example fix

// before (manifest.json)
{"id": "My Boot Screen v1.0!", ...}
// after
{"id": "my-boot-screen-v1", ...}
// directory renamed to match: appearances/my-boot-screen-v1/
Defensive patterns

Strategy: validation

Validate before calling

function isValidAppearanceID(id) {
  return typeof id === "string" && id.length > 0 && id.length <= 64 && /^[a-z0-9][a-z0-9-]*$/.test(id);
}
// only request boot appearances whose ID passes isValidAppearanceID

Type guard

const isAppearanceID = (v: unknown): v is string =>
  typeof v === "string" && v.length <= 64 && /^[a-z0-9][a-z0-9-]*$/.test(v);

Prevention

When it happens

Trigger: GetBootAppearances or getBootAppearanceByID calling loadBootAppearance with an appearanceID containing characters outside the allowed pattern (spaces, slashes, backslashes, non-ASCII), an empty string, or an ID longer than 64 characters. In practice this comes from a persisted selection file or a manifest declaring a boot appearance whose ID violates the pattern.

Common situations: A plugin author names the appearance folder 'My Boot Screen! v1' instead of a slug like 'my-boot-screen'; a copied manifest keeps an old ID with trailing whitespace; a corrupted boot-appearance.json selection file yields a malformed ID (this is usually caught earlier and mapped to ErrBootAppearanceNotFound, but loadBootAppearance re-checks defensively).

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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