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

  1. Grant the SiYuan process read/execute permission on the reported subdirectory
  2. Ensure the vault is on a stable local disk, not a removable or paused cloud-sync mount
  3. Close tools that may delete/move vault directories during the import and retry
  4. 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

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


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)