siyuan-note/siyuan · error

Obsidian Vault is unreadable: read Vault root: %v

Error message

Obsidian Vault is unreadable: read Vault root: %v

What it means

A wrapped variant of errObsidianVaultUnreadable (line 566-568): returned when os.Lstat(abs) fails on the resolved Vault root path. This means the path does not exist, is permission-denied, or otherwise cannot be stat'd by the OS. The error wraps the unreadable sentinel with ': read Vault root: <os error>'.

Source

Thrown at kernel/model/import_obsidian.go:568

	ret.Analysis.UnreferencedFileCount = len(ret.ImportAssets) - len(ret.ReferencedAssets)
	if ret.Analysis.UnreferencedFileCount < 0 {
		ret.Analysis.UnreferencedFileCount = 0
	}
	progress(100, "Analysis completed")
	return ret, nil
}

func validateObsidianVaultRoot(localPath string) (string, error) {
	if strings.TrimSpace(localPath) == "" {
		return "", fmt.Errorf("%w: path is empty", errObsidianVaultUnreadable)
	}
	abs, err := filepath.Abs(filepath.Clean(localPath))
	if err != nil {
		return "", fmt.Errorf("%w: normalize Vault path: %v", errObsidianVaultUnreadable, err)
	}
	info, err := os.Lstat(abs)
	if err != nil {
		return "", fmt.Errorf("%w: read Vault root: %v", errObsidianVaultUnreadable, err)
	}
	if !info.IsDir() {
		return "", errObsidianVaultNotDirectory
	}
	if info.Mode()&os.ModeSymlink != 0 || isObsidianResolvedLink(abs) {
		return "", fmt.Errorf("%w: Vault root is a symbolic link or reparse point", errObsidianVaultUnsafePath)
	}
	if util.IsSensitivePath(abs) {
		return "", fmt.Errorf("%w: selected Vault path is sensitive", errObsidianVaultUnsafePath)
	}
	workspace, _ := filepath.Abs(filepath.Clean(util.WorkspaceDir))
	if sameObsidianPath(abs, workspace) || gulu.File.IsSubPath(workspace, abs) || gulu.File.IsSubPath(abs, workspace) {
		return "", fmt.Errorf("%w: Vault root and SiYuan workspace contain each other", errObsidianVaultUnsafePath)
	}
	configPath := filepath.Join(abs, ".obsidian")
	configInfo, statErr := os.Lstat(configPath)
	if statErr != nil {
		if os.IsNotExist(statErr) {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Confirm the directory exists at the exact path passed to the API (check for typos, case sensitivity on Linux).
  2. Verify the SiYuan process has read/list permissions on the path and all parent directories.
  3. If the Vault is on a network/removable share, ensure it is mounted and accessible, then retry.
  4. Re-select the folder via the UI picker to capture the correct, current path.

Example fix

// before: vault moved or mistyped
localPath := "/home/user/MyVualt" // typo
// -> "Obsidian Vault is unreadable: read Vault root: no such file or directory"

// after
localPath := "/home/user/MyVault"
Defensive patterns

Strategy: validation

Validate before calling

// Stat the path to confirm it exists and is accessible
if _, err := os.Lstat(localPath); err != nil {
    return fmt.Errorf("Vault path is not accessible: %w", err)
}

Try / catch

if _, err := model.StartObsidianVaultAnalysis(localPath); err != nil {
    if errors.Is(err, errObsidianVaultUnreadable) && strings.Contains(err.Error(), "read Vault root") {
        return errors.New("the Vault folder does not exist or is not accessible; check the path and permissions")
    }
}

Prevention

When it happens

Trigger: POST /api/import/startObsidianVaultAnalysis with a localPath that resolves to a non-existent path, or a path the SiYuan process lacks permission to stat. os.Lstat returns a non-nil err and the guard fires.

Common situations: The Vault was moved/deleted/renamed between selection and the API call; a typo in the path; permissions deny the SiYuan process access; a network share is offline; on mobile/sandboxed environments the path is outside the accessible scope.

Related errors


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