{"record":{"id":"30b74e43d7ea011a","repo":"siyuan-note/siyuan","slug":"path-belongs-to-encrypted-notebook-s-s","errorCode":null,"errorMessage":"path belongs to encrypted notebook [%s]: %s","messagePattern":"path belongs to encrypted notebook \\[(.+?)\\]: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/cli/cmd/file.go","lineNumber":47,"sourceCode":"\t\"github.com/siyuan-note/siyuan/kernel/model\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nvar fileCmd = &cobra.Command{\n\tUse:   \"file\",\n\tShort: \"Workspace file operations\",\n}\n\nfunc absPath(rel string) (string, error) {\n\trel = filepath.Clean(strings.ReplaceAll(rel, \"/\", string(os.PathSeparator)))\n\tabs := filepath.Join(util.WorkspaceDir, rel)\n\tif !gulu.File.IsSubPath(util.WorkspaceDir, abs) {\n\t\treturn \"\", fmt.Errorf(\"path escapes workspace: %s\", rel)\n\t}\n\tif boxID := model.EncryptedRawPathBoxID(abs); boxID != \"\" {\n\t\treturn \"\", fmt.Errorf(\"path belongs to encrypted notebook [%s]: %s\", boxID, rel)\n\t}\n\treturn abs, nil\n}\n\nvar fileListCmd = &cobra.Command{\n\tUse:   \"list <path>\",\n\tShort: \"List directory contents\",\n\tArgs:  cobra.MinimumNArgs(1),\n\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\tdir, err := absPath(args[0])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tentries, err := os.ReadDir(dir)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/cli/cmd/file.go#L29-L65","documentation":"Thrown by the shared `absPath` helper when the resolved absolute path falls inside an encrypted notebook. `model.EncryptedRawPathBoxID` checks both the path itself and its longest existing parent (to defeat symlink bypass), returning the box ID if the path belongs to a notebook flagged as encrypted. The CLI deliberately blocks raw file access to encrypted notebook data so ciphertext is never read or written outside the decryption layer.","triggerScenarios":"Any `file` subcommand targeting a path under `data/<encryptedBoxID>/...`. The path may look normal but `EncryptedRawPathBoxID` resolves the box ID from the assets path and confirms `IsEncryptedBox(boxID)` is true.","commonSituations":"Forgetting a notebook was encrypted in settings; scripts iterating all notebooks without filtering encrypted ones; trying to grep or copy files out of an encrypted notebook for a backup.","solutions":["Decrypt or disable encryption on the notebook in the GUI before raw CLI access","Use a higher-level command that goes through the decryption layer rather than `file` subcommands","Exclude encrypted notebook IDs from batch file scripts"],"exampleFix":"// before\nsiyuan file read data/20200101-encsecret/doc.sy\n// after\n# Decrypt notebook in the GUI first, or use a model-level command:\n# Settings - Security - disable encryption on that notebook","handlingStrategy":"validation","validationCode":"// Before batch file ops, filter out encrypted notebooks:\nfor _, box := range openNotebooks {\n    if model.IsEncryptedBox(box.ID) { continue }\n    // only operate on box.ID paths\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Track which notebook IDs are encrypted and exclude them from raw file scripts","Prefer model-level commands over `file` subcommands for encrypted notebooks","Decrypt a notebook in the GUI before any raw CLI access to its files"],"tags":["cli","path-security","encryption","validation","notebook","go","siyuan"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}