siyuan-note/siyuan · critical

appearance files not found at [%s]

Error message

appearance files not found at [%s]

What it means

Thrown in `rootCmd.PersistentPreRunE` when `appearance/langs` does not exist under the resolved working directory. The working dir is resolved by `resolveWorkingDir()`, which probes candidate directories relative to the kernel executable (e.g. `resources/`, `app/`). If none contains `appearance/langs`, the resolved dir is empty and `util.WorkingDir` stays at its build-time default, so the langs stat fails. This guards every CLI subcommand except `workspace`, because the kernel cannot initialize i18n/UI assets without the langs directory.

Source

Thrown at kernel/cli/cmd/root.go:75

		sql.FlushQueue()
		return nil
	},
	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
		// workspace 子命令不需要工作空间校验
		if cmd.Parent() != nil && cmd.Parent().Name() == "workspace" {
			return nil
		}

		// 默认工作目录取内核可执行文件所在目录的上一级(打包后的 resources/,appearance/、stage/ 所在目录),
		// 而非内核可执行文件所在目录本身(resources/kernel/)。resolveWorkingDir() 会校验 appearance/langs 实际存在,
		// 兼容开发态等多种目录布局。
		if workingDir := resolveWorkingDir(); workingDir != "" {
			util.WorkingDir = workingDir
		}

		langsDir := filepath.Join(util.WorkingDir, "appearance", "langs")
		if _, err := os.Stat(langsDir); os.IsNotExist(err) {
			return fmt.Errorf("appearance files not found at [%s]", langsDir)
		}

		// 设置工作空间路径
		if workspacePath == "" {
			workspacePath = os.Getenv("SIYUAN_WORKSPACE_PATH")
		}
		if workspacePath == "" {
			workspacePath = filepath.Join(util.HomeDir, "SiYuan")
		}

		if _, err := os.Stat(workspacePath); os.IsNotExist(err) {
			return fmt.Errorf("directory not found: %s", workspacePath)
		}
		if !util.IsWorkspaceDir(workspacePath) {
			return fmt.Errorf("not a valid workspace: %s", workspacePath)
		}

		util.Mode = "prod"

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Run the kernel binary from its installed location (inside `resources/kernel/` for releases) so `resolveWorkingDir` finds `../appearance/langs`.
  2. For development, ensure `app/appearance/langs` exists and invoke the binary from a path where one of the candidate probes reaches `app/` (e.g. build output under `kernel/`).
  3. Verify the install: check that `<installDir>/appearance/langs/en.json` exists; re-extract the release archive if it is missing.
  4. On macOS, confirm the `.app` bundle's `Contents/Resources/appearance/langs` is present.

Example fix

// before: invoked from /tmp after copying the binary out
/tmp/SiYuan-Kernel search foo
// after: run from the bundled resources layout
/opt/siyuan/resources/kernel/SiYuan-Kernel search foo
Defensive patterns

Strategy: validation

Validate before calling

// Verify appearance/langs exists under the install root before invoking the kernel.
langsDir := filepath.Join(installDir, "appearance", "langs")
if fi, err := os.Stat(langsDir); err != nil || !fi.IsDir() {
    log.Fatalf("appearance/langs missing under %s; reinstall or run from the bundled resources dir", installDir)
}

Prevention

When it happens

Trigger: Running the kernel binary from an unpackaged or relocated location where `appearance/langs/` is not a sibling/parent of the executable; running a raw `go run`/built binary outside the repo layout; a broken install where `resources/` was not extracted; symlinks that defeat `resolveWorkingDir`'s candidate paths.

Common situations: Development builds invoked from a directory not covered by the four candidate probes (`exeDir/..`, `exeDir/../app`, `exeDir/app`, `exeDir/../../app`, plus macOS `Resources`); moving the kernel binary out of its `resources/kernel/` folder; partial extraction of a release archive that omitted `appearance/`.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/0a738c489fb3a09d. Report an issue: GitHub.