siyuan-note/siyuan · error

Obsidian Vault path is unsafe

Error message

Obsidian Vault path is unsafe

What it means

`errObsidianVaultUnsafePath` is returned when the Vault root is rejected as unsafe: it is a symbolic link/reparse point (`isObsidianResolvedLink`), or `util.IsSensitivePath(abs)` matches a protected location. It maps to i18n key 338 and is a deliberate security guard against importing from linked or sensitive filesystem locations.

Solutions

  1. Pass the real (physically resolved) Vault directory instead of a symlink — resolve the link and use its target
  2. Move the Vault out of any sensitive/protected location `util.IsSensitivePath` blocks
  3. On Windows, use the real folder rather than a junction/reparse point

Example fix

// before
validateObsidianVaultRoot("/home/user/vault-link") // symlink -> errObsidianVaultUnsafePath
// after
validateObsidianVaultRoot("/home/user/real/MyVault") // resolved physical directory
Defensive patterns

Strategy: validation

Validate before calling

const real = await fs.realpath(vaultPath);
const stat = await fs.lstat(vaultPath);
if (stat.isSymbolicLink()) {
  throw new Error("Use the resolved physical path, not a symlink: " + real);
}

Type guard

function isRealDirectory(stat: { isDirectory(): boolean; isSymbolicLink(): boolean }): boolean {
  return stat.isDirectory() && !stat.isSymbolicLink();
}

Try / catch

try {
  await api.importObsidianVault(vaultPath);
} catch (e) {
  if (isVaultUnsafePath(e)) {
    // resolve symlink to its target and/or move out of the sensitive path, then retry
  }
}

Prevention

When it happens

Trigger: `validateObsidianVaultRoot` sees `info.Mode()&os.ModeSymlink != 0` or a resolved link, or the absolute path hits `util.IsSensitivePath` (e.g. inside the workspace, system directories).

Common situations: Vault accessed through a symlink to sync storage (Dropbox/iCloud junction); on Windows a junction/reparse point; user points at a protected path like the SiYuan workspace itself.

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@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/ef306ec13c446c9b. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/import_obsidian.go:232

func (err *obsidianUserError) Error() string {
	return err.Cause.Error()
}

func (err *obsidianUserError) Unwrap() error {
	return err.Cause
}

func newObsidianUserError(detailLanguage int, relPath string, cause error) error {
	return &obsidianUserError{DetailLanguage: detailLanguage, RelPath: relPath, Cause: cause}
}

var (
	obsidianTasksMu                 sync.Mutex
	obsidianTasks                   = map[string]*obsidianTask{}
	obsidianActive                  string
	errObsidianVaultUnreadable      = errors.New("Obsidian Vault is unreadable")
	errObsidianVaultNotDirectory    = errors.New("Obsidian Vault path is not a directory")
	errObsidianVaultUnsafePath      = errors.New("Obsidian Vault path is unsafe")
	errObsidianVaultConfigMissing   = errors.New("Obsidian Vault config directory is missing")
	errObsidianVaultMarkdownMissing = errors.New("Obsidian Vault has no readable Markdown")
	errObsidianSourceChanged        = errors.New("Obsidian source file changed")

	obsidianBlockIDPattern  = regexp.MustCompile(`(?m)(?:^|[ \t])\^([A-Za-z0-9-]+)[ \t]*$`)
	obsidianQuotePattern    = regexp.MustCompile(`^((?:[ \t]*>[ \t]?)+)(.*)$`)
	obsidianListItemPattern = regexp.MustCompile(`^([ \t]*(?:[-+*]|\d+[.)])[ \t]+)(.*)$`)
	obsidianFootnotePattern = regexp.MustCompile(`(?m)\[\^[^\]\r\n]+\]`)
)

func StartObsidianVaultAnalysis(localPath string) (*ObsidianVaultTask, error) {
	var replacedTaskID string
	obsidianTasksMu.Lock()
	if obsidianActive != "" {
		if active := obsidianTasks[obsidianActive]; active != nil && !isObsidianTerminalState(active.State) {
			if !isObsidianPreImportState(active.State) {
				obsidianTasksMu.Unlock()
				return nil, errors.New(Conf.Language(329))

View on GitHub (pinned to 9f775e8a12)