siyuan-note/siyuan · error
Obsidian Vault path is unsafe: Vault root is a symbolic link
Error message
Obsidian Vault path is unsafe: Vault root is a symbolic link or reparse point
What it means
A wrapped variant of errObsidianVaultUnsafePath (line 573-574): returned when the Vault root is a symbolic link or a Windows reparse point. The check tests both the Lstat Mode()&os.ModeSymlink flag and isObsidianResolvedLink(abs), which follows links that Lstat alone might miss. The error wraps the unsafe sentinel with ': Vault root is a symbolic link or reparse point'. This prevents symlink-based traversal attacks where a link escapes the intended directory.
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 251596fc0d)
Solutions
- Resolve the symlink to its real target with filepath.EvalSymlinks and pass the physical directory.
- Replace the symlink/junction with the actual Vault directory, or copy the Vault contents to a non-linked folder.
- Select the real underlying folder in the UI picker rather than the link.
Example fix
// before
localPath := "/home/user/vault-link" // symlink
// -> "Obsidian Vault path is unsafe: Vault root is a symbolic link or reparse point"
// after
real, _ := filepath.EvalSymlinks("/home/user/vault-link")
localPath := real // e.g. /mnt/data/MyVault Defensive patterns
Strategy: validation
Validate before calling
// Resolve symlinks before calling the API
real, err := filepath.EvalSymlinks(localPath)
if err != nil { return err }
info, err := os.Lstat(real)
if err != nil { return err }
if info.Mode()&os.ModeSymlink != 0 { return errors.New("path is still a symlink after resolution") } Try / catch
if _, err := model.StartObsidianVaultAnalysis(localPath); err != nil {
if errors.Is(err, errObsidianVaultUnsafePath) && strings.Contains(err.Error(), "symbolic link") {
return errors.New("the Vault root is a symlink; please select the real folder")
}
} Prevention
- Resolve symlinks/junctions to their physical target before importing.
- Avoid organizing Vaults via symlinks; use real directories.
- Select the underlying physical folder in the picker.
When it happens
Trigger: POST /api/import/startObsidianVaultAnalysis with a localPath that is a symlink (Unix) or junction/reparse point (Windows), or resolves through isObsidianResolvedLink to a link target. Line 573 fires.
Common situations: User organized Vaults via symlinks; a cloud-storage client created a symlinked folder; on macOS, some folder arrangements under /Volumes use firmlinks; the Vault root is a Windows junction.
Related errors
- Obsidian Vault path is unsafe
- Obsidian Vault path is unsafe: selected Vault path is sensit
- Obsidian Vault path is unsafe: Vault root and SiYuan workspa
- invalid import token
- import path is not sub path of import dir
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/33cb8adb26bfab30.
Report an issue: GitHub.