{"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":"validation","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/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/import_obsidian.go#L213-L249","documentation":"A sentinel error returned when the Vault root path exists but is not a directory (e.g. it is a regular file). It is produced directly in validateObsidianVaultRoot at the `!info.IsDir()` check, without additional wrapping. The obsidianVaultErrorLanguage helper maps it to i18n key 337 for user-facing display.","triggerScenarios":"POST /api/import/startObsidianVaultAnalysis is called with a localPath that resolves to a file (a .md file, a .zip archive, etc.) rather than a directory. os.Lstat succeeds but info.IsDir() is false.","commonSituations":"User selects a single Markdown file or a ZIP export instead of the Vault folder in the file picker; a path typo points at a file with the same base name; the Vault was exported as a single archive that has not been unpacked.","solutions":["Point localPath at the Vault root folder (the directory containing the .obsidian subfolder), not at a file inside or outside it.","If the Vault arrived as a ZIP, extract it to a directory first, then pass that directory's path.","In the UI file picker, ensure a folder/directory is selected, not an individual file."],"exampleFix":"// before: user passes a file path\nlocalPath := \"/home/user/MyVault.md\"\n// -> errObsidianVaultNotDirectory\n\n// after: pass the directory containing .obsidian\nlocalPath := \"/home/user/MyVault\"","handlingStrategy":"validation","validationCode":"// Ensure the path is a directory before calling analysis\nfunc ensureIsDir(p string) error {\n    info, err := os.Stat(p)\n    if err != nil { return err }\n    if !info.IsDir() { return errors.New(\"path is not a directory\") }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := model.StartObsidianVaultAnalysis(localPath); err != nil {\n    if errors.Is(err, errObsidianVaultNotDirectory) {\n        return errors.New(\"please select the Vault folder, not a file\")\n    }\n}","preventionTips":["Restrict the UI folder picker to directory selection only.","Reject file paths (non-directory) on the client before submitting.","If importing from an archive, extract it to a folder first."],"tags":["obsidian","import","filesystem","validation","sentinel-error"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}