{"record":{"id":"f6d36f36c5dd735e","repo":"siyuan-note/siyuan","slug":"obsidian-vault-path-is-unsafe-vault-root-and-siyu","errorCode":null,"errorMessage":"Obsidian Vault path is unsafe: Vault root and SiYuan workspace contain each other","messagePattern":"Obsidian Vault path is unsafe: Vault root and SiYuan workspace contain each other","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/import_obsidian.go","lineNumber":581,"sourceCode":"\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"%w: normalize Vault path: %v\", errObsidianVaultUnreadable, err)\n\t}\n\tinfo, err := os.Lstat(abs)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"%w: read Vault root: %v\", errObsidianVaultUnreadable, err)\n\t}\n\tif !info.IsDir() {\n\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","sourceCodeStart":563,"sourceCodeEnd":599,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/import_obsidian.go#L563-L599","documentation":"A wrapped variant of errObsidianVaultUnsafePath (line 579-581): returned when the Vault root is the same as, a parent of, or a child of the SiYuan workspace directory. The check uses sameObsidianPath and gulu.File.IsSubPath in both directions to prevent recursive or destructive directory relationships. Importing a Vault that overlaps the workspace would cause SiYuan to scan its own data directory, leading to infinite recursion or data corruption. The error wraps the unsafe sentinel with ': Vault root and SiYuan workspace contain each other'.","triggerScenarios":"POST /api/import/startObsidianVaultAnalysis where abs == workspace, workspace is under abs (Vault is a parent of workspace), or abs is under workspace (Vault is inside the SiYuan data dir). Any of the three IsSubPath/sameObsidianPath checks at line 580 returns true.","commonSituations":"User placed the Obsidian Vault inside the SiYuan workspace/data folder; the workspace was set to the Vault's parent; the user confused the two directories and selected the workspace itself as the Vault; a default workspace path happens to contain the Vault.","solutions":["Move the Vault to a directory completely outside the SiYuan workspace tree.","If the workspace was set inside the Vault, change the SiYuan workspace to a different location.","Use a copy of the Vault in a neutral directory (e.g. /tmp or ~/Imports) for import.","Confirm the two paths share no ancestor/descendant relationship before importing."],"exampleFix":"// before: vault is inside the workspace\n// workspace = /home/user/SiYuan/data\n// vault    = /home/user/SiYuan/data/MyVault\n// -> \"Obsidian Vault path is unsafe: Vault root and SiYuan workspace contain each other\"\n\n// after: vault outside workspace\n// vault = /home/user/ObsidianVaults/MyVault","handlingStrategy":"validation","validationCode":"// Ensure no overlap between Vault root and workspace\nabs, _ := filepath.Abs(filepath.Clean(localPath))\nws, _ := filepath.Abs(filepath.Clean(util.WorkspaceDir))\nif abs == ws || gulu.File.IsSubPath(ws, abs) || gulu.File.IsSubPath(abs, ws) {\n    return errors.New(\"Vault path overlaps the SiYuan workspace; choose a separate directory\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := model.StartObsidianVaultAnalysis(localPath); err != nil {\n    if errors.Is(err, errObsidianVaultUnsafePath) && strings.Contains(err.Error(), \"contain each other\") {\n        return errors.New(\"the Vault overlaps the SiYuan workspace; move it to a separate directory\")\n    }\n}","preventionTips":["Keep the Vault completely outside the SiYuan workspace directory tree.","Do not set the SiYuan workspace to the Vault's parent.","Copy the Vault to a neutral location if it currently overlaps the workspace."],"tags":["obsidian","import","security","validation","workspace","path-containment"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}