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
- Replace the symlink with a real copy of the encrypted document file (cp -L) so it is a regular file inside the box.
- Remove the symlink if it is not a valid document, then re-open/rebuild the indexes.
- 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
- Copy workspace data with full-copy modes, never symlinks (avoid cp -s, rsync --link)
- Audit data/boxes directories for symlinks before opening encrypted boxes
- Keep documents physically inside the box directory
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
- symlink resolves into encrypted notebook [%s]
- refuse to write decrypted asset inside workspace
- encrypted .sy [%s]: base id [%s] != root id [%s]
- asset path contains an unresolved symbolic link [%s]
- resolve assets directory [%s] failed: %w
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/ddd32788af512dbb.
Report an issue: GitHub.