siyuan-note/siyuan · error

encrypted document is a symbolic link [%s]

Error message

encrypted document is a symbolic link [%s]

What it means

While re-authenticating/rebuilding encrypted indexes, the walker rejects any *.sy entry whose filesystem mode reports a symbolic link. Encrypted notebooks require real regular files: a symlinked document would bypass the per-file encryption/authentication model and could point outside the box, so it is treated as an integrity violation and aborts the index rebuild.

Source

Thrown at kernel/model/encrypted_index.go:64

func authenticateEncryptedIndexDocuments(boxID string, dek []byte) error {
	boxDir := filepath.Join(util.DataDir, boxID)
	ids := map[string]struct{}{}
	return filepath.WalkDir(boxDir, func(filePath string, entry fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if entry.IsDir() {
			if filePath != boxDir && !ast.IsNodeIDPattern(entry.Name()) {
				return filepath.SkipDir
			}
			return nil
		}
		if !strings.HasSuffix(entry.Name(), ".sy") {
			return nil
		}
		if entry.Type()&fs.ModeSymlink != 0 {
			return fmt.Errorf("encrypted document is a symbolic link [%s]", entry.Name())
		}
		data, err := filelock.ReadFile(filePath)
		if err != nil {
			return err
		}
		plain, err := DecryptFile(boxID, entry.Name(), dek, data)
		if err != nil {
			return err
		}
		tree, err := loadTreeByData0(plain)
		if err != nil {
			return err
		}
		if tree == nil || tree.Root == nil || tree.Root.ID+".sy" != entry.Name() {
			return fmt.Errorf("encrypted document root ID does not match filename [%s]", entry.Name())
		}
		if _, exists := ids[tree.Root.ID]; exists {
			return fmt.Errorf("duplicate encrypted document ID [%s]", tree.Root.ID)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Replace the symlink with a real copy of the encrypted document file (cp -L) so it is a regular file inside the box.
  2. Remove the symlink if it is not a valid document, then re-open/rebuild the indexes.
  3. Avoid tools that create symlinks when copying or restoring workspace data; use full-copy modes (cp -a, rsync -a without --link).

Example fix

// before (shell)
ln -s /other/workspace/doc.sy data/boxes/<box>/doc.sy
// after
cp -L /other/workspace/doc.sy data/boxes/<box>/doc.sy
Defensive patterns

Strategy: validation

Validate before calling

info, _ := os.Lstat(docPath)
if info != nil && info.Mode()&os.ModeSymlink != 0 {
    return fmt.Errorf("%s is a symlink; replace with a real copy", docPath)
}

Prevention

When it happens

Trigger: A .sy file inside the encrypted box's documents directory is replaced by (or is) a symlink — e.g. a user symlinked a document from elsewhere, a sync/restore tool created links, or a container mount produced link-style entries — while authenticateEncryptedIndexDocuments walks the tree.

Common situations: Manual workspace surgery where users link documents between notebooks; dotfile-managers or restore scripts that symlink data; copying a workspace with cp -s / rsync --link; Docker volume mounts exposing links.

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/ddd32788af512dbb. Report an issue: GitHub.