siyuan-note/siyuan · error · errObsidianVaultUnsafePath
%w: Vault root is a symbolic link or reparse point
Error message
%w: Vault root is a symbolic link or reparse point
What it means
Security guard: the vault root itself is a symbolic link (or a Windows reparse point that resolves elsewhere). The kernel refuses to import through a symlinked root to prevent path escape or double-counting, wrapping errObsidianVaultUnsafePath with 'Vault root is a symbolic link or reparse point'.
Source
Thrown at kernel/model/import_obsidian.go:574
}
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) {
return "", errObsidianVaultConfigMissing
}
return "", fmt.Errorf("%w: read Vault config directory: %v", errObsidianVaultUnreadable, statErr)
}
if !configInfo.IsDir() || configInfo.Mode()&os.ModeSymlink != 0 || isObsidianResolvedLink(configPath) {
return "", errObsidianVaultConfigMissingView on GitHub (pinned to 8641553a1f)
Solutions
- Select the real vault directory (the one containing .obsidian) instead of the symlink/alias
- Resolve the symlink manually and pass its target as localPath
- On Windows, avoid junction/reparse-point folders and choose the physical path
- If a symlink is genuinely needed, restructure so the vault is the physical directory
Example fix
// before
analyzeObsidianVault("/Users/me/vault-link") // symlink
// after
target, _ := filepath.EvalSymlinks("/Users/me/vault-link")
analyzeObsidianVault(target) Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Lstat(vaultPath)
if err == nil && info.Mode()&os.ModeSymlink != 0 {
vaultPath, _ = filepath.EvalSymlinks(vaultPath)
} Type guard
func isRealDir(p string) bool {
info, err := os.Lstat(p)
return err == nil && info.IsDir() && info.Mode()&os.ModeSymlink == 0
} Try / catch
_, err := AnalyzeObsidianVault(vaultPath)
if err != nil && strings.Contains(err.Error(), "symbolic link or reparse point") {
target, _ := filepath.EvalSymlinks(vaultPath)
AnalyzeObsidianVault(target)
} Prevention
- Pick the physical vault directory, not aliases/shortcuts
- Resolve symlinks before submitting the path
- Avoid junction points on Windows for vault storage
When it happens
Trigger: validateObsidianVaultRoot at kernel/model/import_obsidian.go:574 detects info.Mode()&os.ModeSymlink != 0 or isObsidianResolvedLink(abs) on the chosen vault root.
Common situations: User selects a shortcut/symlink alias to the vault instead of the real directory; Windows library/junction reparse points; macOS Finder aliases backed by symlinks; vault symlinked into a synced folder.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- %w: selected Vault path is sensitive
- symlink resolves into encrypted notebook [%s]
- archive entry resolves outside destination [%s]
- marketplace package contains an unsupported file
- symlink escapes workspace: %s
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/a6c6941f62d3e07f.
Report an issue: GitHub.