siyuan-note/siyuan · error

read-only mode

Error message

read-only mode

What it means

SetBootAppearance modifies the persisted boot (splash) appearance selection, but the kernel is running in read-only mode (util.ReadOnly), so configuration writes are disabled and the call returns the default selection with this error. Read-only mode is a runtime state (e.g. readonly launch flag / protected environments) in which no data or config mutation is permitted.

Source

Thrown at kernel/model/boot_appearance.go:214

// GetBootAppearance 返回当前可用的启动页外观,任何异常和安全模式下均回退默认。
func GetBootAppearance() *BootAppearance {
	if util.SafeMode {
		return &BootAppearance{Enabled: false}
	}
	selection, appearance, err := loadSelectedBootAppearance()
	if err != nil {
		return &BootAppearance{Enabled: false}
	}
	if selection.Provider == "" || selection.Appearance == "" {
		return &BootAppearance{Enabled: false}
	}
	return appearance
}

// SetBootAppearance 校验并原子持久化启动页外观选择,两项均为空表示恢复默认。
func SetBootAppearance(provider, appearanceID string) (ret BootAppearanceSelection, err error) {
	if util.ReadOnly {
		return defaultBootAppearanceSelection(), errors.New("read-only mode")
	}
	provider = strings.TrimSpace(provider)
	appearanceID = strings.TrimSpace(appearanceID)
	ret = defaultBootAppearanceSelection()
	if (provider == "") != (appearanceID == "") {
		err = errors.New("provider and appearance must both be empty or non-empty")
		return
	}
	if provider != "" {
		if !bazaar.IsValidPackageName(provider) || !isValidBootAppearanceID(appearanceID) {
			err = ErrBootAppearanceNotFound
			return
		}
		if _, resolveErr := getBootAppearanceByID(provider, appearanceID); resolveErr != nil {
			err = ErrBootAppearanceNotFound
			return
		}
		ret.Provider, ret.Appearance = provider, appearanceID

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Restart the kernel without read-only mode, then call SetBootAppearance.
  2. Skip the customization step when util.ReadOnly is true; treat the default selection as final.
  3. If read-only is unintentional, check the launch configuration/flags that set util.ReadOnly.
  4. Use the returned defaultBootAppearanceSelection() as a safe fallback in callers.

Example fix

// before
_, err := model.SetBootAppearance(provider, id)
// after
if util.ReadOnly {
    return model.DefaultBootAppearanceSelection() // skip write in read-only mode
}
_, err := model.SetBootAppearance(provider, id)
Defensive patterns

Strategy: fallback

Validate before calling

if (kernelReadOnly) skipAppearanceCustomization()

Try / catch

sel, err := model.SetBootAppearance(provider, id)
if err != nil && strings.Contains(err.Error(), "read-only mode") {
    sel = model.DefaultBootAppearanceSelection()
}

Prevention

When it happens

Trigger: Calling SetBootAppearance (via setBootAppearance API or tests) while the kernel runs with read-only mode enabled; attempting to persist any boot appearance selection in a locked/readonly workspace.

Common situations: Kernel started with the readonly flag for kiosk/public deployments; automated tooling trying to customize the splash screen on a read-only instance.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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