siyuan-note/siyuan · error
read Vault directory
Error message
read Vault directory [%s]: %w
What it means
os.ReadDir on a directory inside the Vault failed while scanning Vault files, so the import analysis aborts. The message carries the vault-relative directory and the wrapped OS error so the user can see which directory could not be listed.
Solutions
- Grant the SiYuan process read/execute permission on the reported subdirectory
- Ensure the vault is on a stable local disk, not a removable or paused cloud-sync mount
- Close tools that may delete/move vault directories during the import and retry
- Check the wrapped OS error in the message (e.g. 'permission denied' vs 'no such file') to target the fix
Defensive patterns
Strategy: try-catch
Validate before calling
const fs = require('fs');
for (const dir of allVaultSubdirs) fs.accessSync(dir, fs.constants.R_OK | fs.constants.X_OK); Try / catch
try {
await importObsidianVault(vaultPath);
} catch (e) {
const m = /read Vault directory \[([^\]]+)\]/.exec(e.message);
if (m) console.error('Fix access to', m[1]);
} Prevention
- Keep the vault on a stable local filesystem, not a removable or paused cloud-sync mount
- Grant recursive read/execute permission on the vault before import
- Don't delete or move vault folders while an import is running
When it happens
Trigger: scanObsidianVaultFiles calls os.ReadDir(absDir) and the directory exists in the walk (was listed by its parent) but cannot be opened: EACCES, the entry is a special/non-directory file that claimed to be a dir, or the directory disappeared between parent listing and ReadDir.
Common situations: Permission differences between directories in the same vault; the vault is on a removable/network drive that dropped out mid-scan; antivirus or sync tools (OneDrive/Dropbox placeholders) interfering; files being deleted while importing.
Related errors
- : read Vault config directory
- 345
- Conf.Language(14) (copy resource failed: )
- create AI editor actions directory failed
- create conf dir failed
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/6ffe76876f1b349c.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/import_obsidian.go:603
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 {
return err
}
name := entry.Name()
rel := path.Join(relDir, filepath.ToSlash(name))
abs := filepath.Join(absDir, name)
if strings.HasPrefix(name, ".") {
if relDir == "" && name == ".obsidian" {
continue
}
vault.Analysis.SkippedHiddenCount++
continue
}
entryInfo, infoErr := entry.Info()
if infoErr != nil {View on GitHub (pinned to 9f775e8a12)