siyuan-note/siyuan · error

path belongs to encrypted notebook

Error message

path belongs to encrypted notebook [%s]: %s

What it means

absPath additionally refuses paths that belong to an encrypted notebook. model.EncryptedRawPathBoxID checks whether the resolved absolute path lies inside a notebook that stores raw encrypted data; touching such paths via generic file commands would bypass the notebook's encryption envelope, so the helper returns this error identifying the box ID.

Solutions

  1. Operate on encrypted notebooks only through document-level APIs/commands (export, editor) rather than raw file commands.
  2. Check which notebooks are encrypted and exclude their paths from file scripts.
  3. If the notebook should not be encrypted, create/keep the data in a regular notebook and migrate documents via supported export/import.
  4. Resolve the box ID from the error message to identify which notebook is encrypted.

Example fix

// before
siyuan file list notebooks/20240101120000-enc123/doc.sy
// error: path belongs to encrypted notebook [20240101120000-enc123]: ...

// after
siyuan export sy --id 20240501120000-xyz9876 --output doc.sy.zip
Defensive patterns

Strategy: try-catch

Validate before calling

// Skip encrypted notebooks before touching their paths
for _, box := range boxes {
    if box.Encrypted {
        continue
    }
    // queue file operations for box.ID only
}

Try / catch

abs, err := absPath(rel)
if err != nil {
    if strings.Contains(err.Error(), "path belongs to encrypted notebook") {
        // route through document-level export/API instead of raw file access
        return ErrEncryptedNotebookPath
    }
    return err
}

Prevention

When it happens

Trigger: Running a CLI file command (list/read/write) with a path that resolves inside an encrypted notebook's directory under data/<boxID>/ of a workspace with encrypted notebooks configured.

Common situations: Bulk scripts that walk all notebooks and hit the encrypted one; users unaware a notebook was created with encryption enabled; tools that enumerate data/ directly instead of using document-level APIs.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/30b74e43d7ea011a. Report an issue: GitHub.

Appendix: source

Thrown at kernel/cli/cmd/file.go:47

	"github.com/siyuan-note/siyuan/kernel/model"
	"github.com/siyuan-note/siyuan/kernel/util"

	"github.com/spf13/cobra"
)

var fileCmd = &cobra.Command{
	Use:   "file",
	Short: "Workspace file operations",
}

func absPath(rel string) (string, error) {
	rel = filepath.Clean(strings.ReplaceAll(rel, "/", string(os.PathSeparator)))
	abs := filepath.Join(util.WorkspaceDir, rel)
	if !gulu.File.IsSubPath(util.WorkspaceDir, abs) {
		return "", fmt.Errorf("path escapes workspace: %s", rel)
	}
	if boxID := model.EncryptedRawPathBoxID(abs); boxID != "" {
		return "", fmt.Errorf("path belongs to encrypted notebook [%s]: %s", boxID, rel)
	}
	return abs, nil
}

var fileListCmd = &cobra.Command{
	Use:   "list <path>",
	Short: "List directory contents",
	Args:  cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		dir, err := absPath(args[0])
		if err != nil {
			return err
		}
		entries, err := os.ReadDir(dir)
		if err != nil {
			return err
		}
		w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)

View on GitHub (pinned to 9f775e8a12)