{"record":{"id":"5f439dbda5f7bfa6","repo":"siyuan-note/siyuan","slug":"w-read-vault-config-directory-v","errorCode":null,"errorMessage":"%w: read Vault config directory: %v","messagePattern":"%w: read Vault config directory: (.+?)","errorType":"error_code","errorClass":"errObsidianVaultUnreadable","httpStatus":null,"severity":"error","filePath":"kernel/model/import_obsidian.go","lineNumber":589,"sourceCode":"\t\treturn \"\", errObsidianVaultNotDirectory\n\t}\n\tif info.Mode()&os.ModeSymlink != 0 || isObsidianResolvedLink(abs) {\n\t\treturn \"\", fmt.Errorf(\"%w: Vault root is a symbolic link or reparse point\", errObsidianVaultUnsafePath)\n\t}\n\tif util.IsSensitivePath(abs) {\n\t\treturn \"\", fmt.Errorf(\"%w: selected Vault path is sensitive\", errObsidianVaultUnsafePath)\n\t}\n\tworkspace, _ := filepath.Abs(filepath.Clean(util.WorkspaceDir))\n\tif sameObsidianPath(abs, workspace) || gulu.File.IsSubPath(workspace, abs) || gulu.File.IsSubPath(abs, workspace) {\n\t\treturn \"\", fmt.Errorf(\"%w: Vault root and SiYuan workspace contain each other\", errObsidianVaultUnsafePath)\n\t}\n\tconfigPath := filepath.Join(abs, \".obsidian\")\n\tconfigInfo, statErr := os.Lstat(configPath)\n\tif statErr != nil {\n\t\tif os.IsNotExist(statErr) {\n\t\t\treturn \"\", errObsidianVaultConfigMissing\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"%w: read Vault config directory: %v\", errObsidianVaultUnreadable, statErr)\n\t}\n\tif !configInfo.IsDir() || configInfo.Mode()&os.ModeSymlink != 0 || isObsidianResolvedLink(configPath) {\n\t\treturn \"\", errObsidianVaultConfigMissing\n\t}\n\treturn abs, nil\n}\n\nfunc scanObsidianVaultFiles(ctx context.Context, vault *obsidianVaultContext, relDir, absDir string) error {\n\tif err := ctx.Err(); err != nil {\n\t\treturn err\n\t}\n\tentries, err := os.ReadDir(absDir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read Vault directory [%s]: %w\", relDir, err)\n\t}\n\tsort.Slice(entries, func(i, j int) bool { return strings.ToLower(entries[i].Name()) < strings.ToLower(entries[j].Name()) })\n\tfor _, entry := range entries {\n\t\tif err = ctx.Err(); err != nil {","sourceCodeStart":571,"sourceCodeEnd":607,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/import_obsidian.go#L571-L607","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: unreadable permissions\nchmod 000 ~/.config/obsidian/MyVault/.obsidian\n\n// after\nchmod 755 ~/.config/obsidian/MyVault/.obsidian","handlingStrategy":"validation","validationCode":"import fs from \"fs\"\ntry {\n  const info = fs.lstatSync(path.join(vaultRoot, \".obsidian\"))\n  if (!info.isDirectory()) throw new Error(\".obsidian is not a directory\")\n} catch (e) {\n  throw new Error(`Cannot read vault config directory: ${e.message}`)\n}","typeGuard":null,"tryCatchPattern":"try {\n  fs.accessSync(path.join(vaultRoot, \".obsidian\"), fs.constants.R_OK)\n} catch (e) {\n  logError(\"Vault config directory unreadable\", e)\n  // surface a user-facing message and abort before import\n}","preventionTips":["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"],"tags":["go","filesystem","obsidian-import","permissions"],"backgroundTag":"file-read-failed","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}