siyuan-note/siyuan · error
Obsidian Vault is unreadable: normalize Vault path: %v
Error message
Obsidian Vault is unreadable: normalize Vault path: %v
What it means
A wrapped variant of errObsidianVaultUnreadable (line 562-564): returned when filepath.Abs(filepath.Clean(localPath)) fails. filepath.Abs can fail on Windows when the current working directory drive cannot be determined, or in edge cases with path separators. The error wraps the unreadable sentinel with ': normalize Vault path: <err>'.
Source
Thrown at kernel/model/import_obsidian.go:564
ret.ImportAssets[key] = asset
ret.Analysis.ImportableAssetCount++
ret.Analysis.ImportableAssetSize += asset.Source.Size
}
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)
}View on GitHub (pinned to 251596fc0d)
Solutions
- Pass an already-absolute path to avoid reliance on the process working directory.
- On Windows, ensure the drive hosting the process CWD is still mounted.
- Sanitize the path string to remove NUL bytes or control characters before submitting.
- Retry from a stable working directory (e.g. the SiYuan workspace root).
Example fix
// before: relative path with no resolvable CWD localPath := "./vault" // CWD drive ejected on Windows // -> "Obsidian Vault is unreadable: normalize Vault path: ..." // after: pass absolute path localPath := "D:/Vaults/MyVault"
Defensive patterns
Strategy: validation
Validate before calling
// Pass an absolute path and check it resolves
abs, err := filepath.Abs(filepath.Clean(localPath))
if err != nil { return fmt.Errorf("path cannot be resolved: %w", err) } Try / catch
if _, err := model.StartObsidianVaultAnalysis(localPath); err != nil {
if errors.Is(err, errObsidianVaultUnreadable) && strings.Contains(err.Error(), "normalize Vault path") {
return errors.New("the path could not be resolved to an absolute path; provide a full path")
}
} Prevention
- Always pass an absolute path to avoid CWD dependency.
- On Windows, ensure the CWD drive is mounted before the call.
- Strip NUL/control characters from path strings.
When it happens
Trigger: POST /api/import/startObsidianVaultAnalysis with a path that causes filepath.Abs to error — rare, but possible on Windows when the process working directory is on a removed drive, or with paths containing NUL bytes or other characters invalid for the OS path API.
Common situations: Windows: the removable drive holding the CWD was ejected before the call; the path contains embedded NUL or control characters; an exotic OS-level path resolution failure.
Related errors
- Obsidian Vault is unreadable
- Obsidian Vault path is not a directory
- Obsidian Vault is unreadable: path is empty
- Obsidian Vault is unreadable: read Vault root: %v
- 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/0998d906bacb6fde.
Report an issue: GitHub.