siyuan-note/siyuan · error

Obsidian Vault path is unsafe

Error message

Obsidian Vault path is unsafe

What it means

A sentinel base error for any Vault path deemed unsafe for import. Like the unreadable sentinel, it is wrapped with a descriptive suffix (symbolic link/reparse point, sensitive path, or workspace containment) via fmt.Errorf %w. The obsidianVaultErrorLanguage helper maps it to i18n key 338. It is the parent for errors 777, 778, and 779.

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 251596fc0d)

Solutions

  1. Use the real physical directory of the Vault, not a symlink or junction — resolve the link to its target first.
  2. Avoid selecting the SiYuan workspace folder or any folder that overlaps it; the Vault must be outside the workspace tree.
  3. Move or copy the Vault to a neutral directory if it currently sits inside or contains the workspace.
  4. Check the wrapped suffix to identify which safety rule fired (symlink, sensitive, or containment).

Example fix

// before: symlinked vault
localPath := "/home/user/vault-link" // -> symlink, errObsidianVaultUnsafePath

// after: resolve to real target
real, err := filepath.EvalSymlinks(localPath)
if err != nil { return err }
// pass real to startObsidianVaultAnalysis
Defensive patterns

Strategy: validation

Validate before calling

// Resolve symlinks and check sensitivity/containment before calling the API
func ensureSafeVaultPath(localPath string) error {
    abs, err := filepath.Abs(filepath.Clean(localPath))
    if err != nil { return err }
    info, err := os.Lstat(abs)
    if err != nil { return err }
    if info.Mode()&os.ModeSymlink != 0 { return errors.New("Vault root is a symlink; resolve it first") }
    if util.IsSensitivePath(abs) { return errors.New("Vault path is sensitive") }
    ws, _ := filepath.Abs(filepath.Clean(util.WorkspaceDir))
    if gulu.File.IsSubPath(ws, abs) || gulu.File.IsSubPath(abs, ws) || sameObsidianPath(abs, ws) {
        return errors.New("Vault overlaps the SiYuan workspace")
    }
    return nil
}

Try / catch

if _, err := model.StartObsidianVaultAnalysis(localPath); err != nil {
    if errors.Is(err, errObsidianVaultUnsafePath) {
        // inspect the suffix: symlink, sensitive, or containment
        return fmt.Errorf("Vault path is unsafe (%s); use a real directory outside the workspace", err)
    }
}

Prevention

When it happens

Trigger: Calling startObsidianVaultAnalysis with a Vault root that is a symlink or Windows reparse point (line 573), a path flagged by util.IsSensitivePath (line 576), or a path that contains or is contained within the SiYuan workspace directory (line 580).

Common situations: Selecting a symlinked folder as the Vault; pointing at a system-sensitive path (e.g. /etc, a Windows system directory) that IsSensitivePath blocks; selecting the SiYuan workspace data directory itself or a parent/child of it as the Vault, which would cause recursive or destructive reads.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/ef306ec13c446c9b. Report an issue: GitHub.