{"record":{"id":"45bbcf754091fa2d","repo":"siyuan-note/siyuan","slug":"w-vault-root-and-siyuan-workspace-contain-each-o","errorCode":null,"errorMessage":"%w: Vault root and SiYuan workspace contain each other","messagePattern":"%w: Vault root and SiYuan workspace contain each other","errorType":"error_code","errorClass":"errObsidianVaultUnsafePath","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/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/import_obsidian.go#L563-L599","documentation":"During Obsidian vault import validation, SiYuan rejects the selected vault root if it overlaps the SiYuan workspace directory in either direction: identical paths, vault inside workspace, or workspace inside vault. This prevents the importer from recursively reading/altering the workspace's own data, config, and temp files, and from nesting vault content inside the running workspace. The check runs on absolute, cleaned paths using case-normalized subpath comparison.","triggerScenarios":"Calling the Obsidian import flow with a vault root that equals util.WorkspaceDir, is a parent of it (e.g. selecting the user home directory that contains the workspace folder), or is nested inside the workspace (e.g. selecting <workspace>/data or a notes folder already inside the workspace).","commonSituations":"User picks 'home' or 'Documents' as the vault when SiYuan workspace lives under it; user created Obsidian vault inside <workspace>/data previously; confusing workspace with data directory; running SiYuan portable with workspace next to vaults.","solutions":["Choose a vault root that is neither inside nor an ancestor of the SiYuan workspace directory","If the notes live inside <workspace>/data, use SiYuan's native workspace flow instead of the Obsidian importer","Move the Obsidian vault to a sibling directory outside the workspace before importing","Verify WorkspaceDir (Help - About in the UI) to see which path is conflicting"],"exampleFix":"// before: vault inside workspace\nvaultRoot := filepath.Join(util.WorkspaceDir, \"data\", \"MyVault\")\nerr := importObsidian(vaultRoot)\n\n// after: vault in an independent location\nvaultRoot := \"/home/user/MyVault\"\nerr := importObsidian(vaultRoot)","handlingStrategy":"validation","validationCode":"const abs = path.resolve(vaultRoot)\nconst workspace = path.resolve(getSiYuanWorkspaceDir())\nif (abs === workspace || abs.startsWith(workspace + path.sep) || workspace.startsWith(abs + path.sep)) {\n  throw new Error(\"Vault root and SiYuan workspace overlap; choose a different vault root\")\n}","typeGuard":"function isPathInside(child, parent) {\n  const rel = path.relative(path.resolve(parent), path.resolve(child))\n  return rel !== \"\" && !rel.startsWith(\"..\") && !path.isAbsolute(rel)\n}","tryCatchPattern":null,"preventionTips":["Keep Obsidian vaults in a dedicated directory separate from the SiYuan workspace","Never select the home directory or Documents as a vault root when the workspace lives nearby","Before importing, resolve both paths with filepath.Abs and check containment"],"tags":["go","filesystem","obsidian-import","path-safety"],"backgroundTag":"path-traversal-blocked","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}