{"record":{"id":"2f3dfb53ad96ecde","repo":"siyuan-note/siyuan","slug":"remove-notebook-s-path-s-failed-s","errorCode":null,"errorMessage":"Remove notebook [%s] path [%s] failed: %s","messagePattern":"Remove notebook \\[(.+?)\\] path \\[(.+?)\\] failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/box.go","lineNumber":487,"sourceCode":"\t\tfromDir := filepath.Join(boxLocalPath, oldDir)\n\t\tif util.IsEmptyDir(fromDir) {\n\t\t\tfilelock.Remove(fromDir)\n\t\t}\n\t}\n\tIncSync()\n\treturn nil\n}\n\nfunc (box *Box) Remove(path string) error {\n\tif _, err := box.validateBoxPath(path); err != nil {\n\t\treturn err\n\t}\n\tboxLocalPath := filepath.Join(util.DataDir, box.ID)\n\tfilePath := filepath.Join(boxLocalPath, path)\n\tif err := filelock.Remove(filePath); err != nil {\n\t\tmsg := fmt.Sprintf(Conf.Language(7), box.Name, path, err)\n\t\tlogging.LogErrorf(\"remove [path=%s] in box [%s] failed: %s\", path, box.ID, err)\n\t\treturn errors.New(msg)\n\t}\n\tIncSync()\n\treturn nil\n}\n\nfunc (box *Box) ListFiles(path string) (ret []*FileInfo) {\n\t// ListFiles 委托给 Ls，后者已有 validateBoxPath\n\tfis, _, err := box.Ls(path)\n\tif err != nil {\n\t\treturn\n\t}\n\tbox.listFiles(&fis, &ret)\n\treturn\n}\n\nfunc (box *Box) listFiles(files, ret *[]*FileInfo) {\n\tfor _, file := range *files {\n\t\tif file.isdir {","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/box.go#L469-L505","documentation":"In Box.Remove, after validateBoxPath, filelock.Remove(filePath) is attempted; on failure it logs and returns errors.New(fmt.Sprintf(Conf.Language(7), box.Name, path, err)) — 'Remove notebook [%s] path [%s] failed: %s'. filelock.Remove wraps os.Remove, which removes a single file or empty directory and returns an error for a non-empty directory or a missing/locked path.","triggerScenarios":"POST /api/filetree/removeDoc/removeDocByID/removeDocs (and internal cleanup) removing a path that does not exist, is a non-empty directory (os.Remove rejects those), is locked/open by another process, or lacks delete permission.","commonSituations":"Removing a folder that still contains .sy files (must empty it first); removing a path already deleted by another client; the .sy is open in an editor/sync process on Windows; read-only volume.","solutions":["Tolerate 'not exist' as a no-op success (the desired state is already achieved).","To remove a non-empty directory, delete its contents first (walk and remove children), since os.Remove will not recurse.","Close tools holding the file before removing on Windows.","Confirm delete permission on the notebook data dir."],"exampleFix":"// before\nif err := box.Remove(path); err != nil { return err }\n\n// after: treat already-gone as success\nif err := box.Remove(path); err != nil {\n    if errors.Is(err, os.ErrNotExist) { return nil }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// Tolerate 'not exist' as the desired end state.\nif !box.Exist(path) { return nil }","typeGuard":null,"tryCatchPattern":"// Treat already-gone as success; recurse for non-empty dirs.\nif err := box.Remove(path); err != nil {\n    if errors.Is(err, os.ErrNotExist) { return nil }\n    return err\n}","preventionTips":["Empty a directory before removing it (os.Remove does not recurse).","Close editors/sync clients holding the file on Windows before removing."],"tags":["filesystem","notebook","remove","delete"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}