{"record":{"id":"01657a5e26e18571","repo":"siyuan-note/siyuan","slug":"obsidian-vault-path-is-not-a-directory","errorCode":null,"errorMessage":"Obsidian Vault path is not a directory","messagePattern":"Obsidian Vault path is not a directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/import_obsidian.go","lineNumber":231,"sourceCode":"\nfunc (err *obsidianUserError) Error() string {\n\treturn err.Cause.Error()\n}\n\nfunc (err *obsidianUserError) Unwrap() error {\n\treturn err.Cause\n}\n\nfunc newObsidianUserError(detailLanguage int, relPath string, cause error) error {\n\treturn &obsidianUserError{DetailLanguage: detailLanguage, RelPath: relPath, Cause: cause}\n}\n\nvar (\n\tobsidianTasksMu                 sync.Mutex\n\tobsidianTasks                   = map[string]*obsidianTask{}\n\tobsidianActive                  string\n\terrObsidianVaultUnreadable      = errors.New(\"Obsidian Vault is unreadable\")\n\terrObsidianVaultNotDirectory    = errors.New(\"Obsidian Vault path is not a directory\")\n\terrObsidianVaultUnsafePath      = errors.New(\"Obsidian Vault path is unsafe\")\n\terrObsidianVaultConfigMissing   = errors.New(\"Obsidian Vault config directory is missing\")\n\terrObsidianVaultMarkdownMissing = errors.New(\"Obsidian Vault has no readable Markdown\")\n\terrObsidianSourceChanged        = errors.New(\"Obsidian source file changed\")\n\n\tobsidianBlockIDPattern  = regexp.MustCompile(`(?m)(?:^|[ \\t])\\^([A-Za-z0-9-]+)[ \\t]*$`)\n\tobsidianQuotePattern    = regexp.MustCompile(`^((?:[ \\t]*>[ \\t]?)+)(.*)$`)\n\tobsidianListItemPattern = regexp.MustCompile(`^([ \\t]*(?:[-+*]|\\d+[.)])[ \\t]+)(.*)$`)\n\tobsidianFootnotePattern = regexp.MustCompile(`(?m)\\[\\^[^\\]\\r\\n]+\\]`)\n)\n\nfunc StartObsidianVaultAnalysis(localPath string) (*ObsidianVaultTask, error) {\n\tvar replacedTaskID string\n\tobsidianTasksMu.Lock()\n\tif obsidianActive != \"\" {\n\t\tif active := obsidianTasks[obsidianActive]; active != nil && !isObsidianTerminalState(active.State) {\n\t\t\tif !isObsidianPreImportState(active.State) {\n\t\t\t\tobsidianTasksMu.Unlock()","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/import_obsidian.go#L213-L249","documentation":"`errObsidianVaultNotDirectory` is returned by `validateObsidianVaultRoot` when the given path exists but `stat` shows it is not a directory. It maps to i18n key 337 in `obsidianVaultErrorLanguage`, so the user sees a localized \"Vault path is not a directory\" message.","triggerScenarios":"Calling `ImportObsidianVault` (or its validation endpoint, exercised by `TestObsidianVaultValidationErrors`) with `localPath` pointing to a regular file, symlink to a file, or device node instead of a folder.","commonSituations":"User pasted the path of a note file (`note.md`) instead of the vault folder; a path was mangled by quoting so it points at a file; an old vault file was restored in place of the directory.","solutions":["Pass the Vault root directory (the folder containing `.obsidian/`), not a file inside it","Fix path quoting/escaping so the full directory path is passed","Recreate the directory if the vault folder was replaced by a file"],"exampleFix":"// before\nvalidateObsidianVaultRoot(\"/home/user/MyVault/README.md\") // errObsidianVaultNotDirectory\n// after\nvalidateObsidianVaultRoot(\"/home/user/MyVault\")","handlingStrategy":"validation","validationCode":"const stat = await fs.stat(vaultPath);\nif (!stat.isDirectory()) {\n  throw new Error(`'${vaultPath}' is a file, not a directory`);\n}","typeGuard":"function isDirectoryPath(stat: { isDirectory(): boolean }): boolean {\n  return stat.isDirectory();\n}","tryCatchPattern":"try {\n  await api.importObsidianVault(vaultPath);\n} catch (e) {\n  if (isVaultNotDirectory(e)) {\n    // ask the user to pick the vault folder instead of a note file\n  }\n}","preventionTips":["Directory pickers should be used instead of file pickers for vault selection","Verify with fs.stat().isDirectory() before calling the import API","Watch for quoting bugs that truncate a directory path to a file path"],"tags":["obsidian","import","path-validation"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}