siyuan-note/siyuan · error · errObsidianVaultUnsafePath

%w: Vault root and SiYuan workspace contain each other

Error message

%w: Vault root and SiYuan workspace contain each other

What it means

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.

Source

Thrown at kernel/model/import_obsidian.go:581

	if err != nil {
		return "", fmt.Errorf("%w: normalize Vault path: %v", errObsidianVaultUnreadable, err)
	}
	info, err := os.Lstat(abs)
	if err != nil {
		return "", fmt.Errorf("%w: read Vault root: %v", errObsidianVaultUnreadable, err)
	}
	if !info.IsDir() {
		return "", errObsidianVaultNotDirectory
	}
	if info.Mode()&os.ModeSymlink != 0 || isObsidianResolvedLink(abs) {
		return "", fmt.Errorf("%w: Vault root is a symbolic link or reparse point", errObsidianVaultUnsafePath)
	}
	if util.IsSensitivePath(abs) {
		return "", fmt.Errorf("%w: selected Vault path is sensitive", errObsidianVaultUnsafePath)
	}
	workspace, _ := filepath.Abs(filepath.Clean(util.WorkspaceDir))
	if sameObsidianPath(abs, workspace) || gulu.File.IsSubPath(workspace, abs) || gulu.File.IsSubPath(abs, workspace) {
		return "", fmt.Errorf("%w: Vault root and SiYuan workspace contain each other", errObsidianVaultUnsafePath)
	}
	configPath := filepath.Join(abs, ".obsidian")
	configInfo, statErr := os.Lstat(configPath)
	if statErr != nil {
		if os.IsNotExist(statErr) {
			return "", errObsidianVaultConfigMissing
		}
		return "", fmt.Errorf("%w: read Vault config directory: %v", errObsidianVaultUnreadable, statErr)
	}
	if !configInfo.IsDir() || configInfo.Mode()&os.ModeSymlink != 0 || isObsidianResolvedLink(configPath) {
		return "", errObsidianVaultConfigMissing
	}
	return abs, nil
}

func scanObsidianVaultFiles(ctx context.Context, vault *obsidianVaultContext, relDir, absDir string) error {
	if err := ctx.Err(); err != nil {
		return err

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Choose a vault root that is neither inside nor an ancestor of the SiYuan workspace directory
  2. If the notes live inside <workspace>/data, use SiYuan's native workspace flow instead of the Obsidian importer
  3. Move the Obsidian vault to a sibling directory outside the workspace before importing
  4. Verify WorkspaceDir (Help - About in the UI) to see which path is conflicting

Example fix

// before: vault inside workspace
vaultRoot := filepath.Join(util.WorkspaceDir, "data", "MyVault")
err := importObsidian(vaultRoot)

// after: vault in an independent location
vaultRoot := "/home/user/MyVault"
err := importObsidian(vaultRoot)
Defensive patterns

Strategy: validation

Validate before calling

const abs = path.resolve(vaultRoot)
const workspace = path.resolve(getSiYuanWorkspaceDir())
if (abs === workspace || abs.startsWith(workspace + path.sep) || workspace.startsWith(abs + path.sep)) {
  throw new Error("Vault root and SiYuan workspace overlap; choose a different vault root")
}

Type guard

function isPathInside(child, parent) {
  const rel = path.relative(path.resolve(parent), path.resolve(child))
  return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel)
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/45bbcf754091fa2d. Report an issue: GitHub.