siyuan-note/siyuan · error · errObsidianVaultUnreadable
%w: read Vault config directory: %v
Error message
%w: read Vault config directory: %v
What it means
After locating the vault root, the importer must read its `.obsidian` configuration directory to confirm this is a real Obsidian vault. When `os.Lstat("<vault>/.obsidian")` fails with an error other than not-exist (e.g. permission denied, I/O error, or a filesystem-level failure), the import aborts with this wrapped error. A not-exist error is reported separately as errObsidianVaultConfigMissing.
Source
Thrown at kernel/model/import_obsidian.go:589
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 "", errObsidianVaultConfigMissing
}
return abs, nil
}
func scanObsidianVaultFiles(ctx context.Context, vault *obsidianVaultContext, relDir, absDir string) error {
if err := ctx.Err(); err != nil {
return err
}
entries, err := os.ReadDir(absDir)
if err != nil {
return fmt.Errorf("read Vault directory [%s]: %w", relDir, err)
}
sort.Slice(entries, func(i, j int) bool { return strings.ToLower(entries[i].Name()) < strings.ToLower(entries[j].Name()) })
for _, entry := range entries {
if err = ctx.Err(); err != nil {View on GitHub (pinned to 8641553a1f)
Solutions
- Check that `<vault>/.obsidian` exists and is readable: run `ls -la <vault>/.obsidian`
- Fix directory/file permissions so the user running SiYuan can stat and read the path
- Remount the drive the vault lives on if it is external or networked
- Exclude the vault from antivirus/quarantine tools that lock metadata access
Example fix
// before: unreadable permissions chmod 000 ~/.config/obsidian/MyVault/.obsidian // after chmod 755 ~/.config/obsidian/MyVault/.obsidian
Defensive patterns
Strategy: validation
Validate before calling
import fs from "fs"
try {
const info = fs.lstatSync(path.join(vaultRoot, ".obsidian"))
if (!info.isDirectory()) throw new Error(".obsidian is not a directory")
} catch (e) {
throw new Error(`Cannot read vault config directory: ${e.message}`)
} Try / catch
try {
fs.accessSync(path.join(vaultRoot, ".obsidian"), fs.constants.R_OK)
} catch (e) {
logError("Vault config directory unreadable", e)
// surface a user-facing message and abort before import
} Prevention
- Verify `.obsidian` exists and is readable before selecting a vault
- Keep vaults off flaky network/external drives when importing
- Check permissions (755 on directories) and antivirus quarantine status
When it happens
Trigger: os.Lstat on `<vaultRoot>/.obsidian` returns any error except os.IsNotExist: read permission denied on the vault directory, I/O error on the filesystem, name too long, or the Lstat syscall failing for other OS reasons.
Common situations: Vault on an external/network drive that disconnected; directory permissions changed by sync or antivirus tooling; vault mounted read-only with restrictive ACLs; a broken fuse mount.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- read session file failed: %w
- create session dir failed: %w
- %w: Vault root and SiYuan workspace contain each other
- read Vault directory [%s]: %w
- read Vault path [%s]: %w
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/5f439dbda5f7bfa6.
Report an issue: GitHub.