siyuan-note/siyuan · error
Remove notebook [%s] path [%s] failed: %s
Error message
Remove notebook [%s] path [%s] failed: %s
What it means
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.
Source
Thrown at kernel/model/box.go:487
fromDir := filepath.Join(boxLocalPath, oldDir)
if util.IsEmptyDir(fromDir) {
filelock.Remove(fromDir)
}
}
IncSync()
return nil
}
func (box *Box) Remove(path string) error {
if _, err := box.validateBoxPath(path); err != nil {
return err
}
boxLocalPath := filepath.Join(util.DataDir, box.ID)
filePath := filepath.Join(boxLocalPath, path)
if err := filelock.Remove(filePath); err != nil {
msg := fmt.Sprintf(Conf.Language(7), box.Name, path, err)
logging.LogErrorf("remove [path=%s] in box [%s] failed: %s", path, box.ID, err)
return errors.New(msg)
}
IncSync()
return nil
}
func (box *Box) ListFiles(path string) (ret []*FileInfo) {
// ListFiles 委托给 Ls,后者已有 validateBoxPath
fis, _, err := box.Ls(path)
if err != nil {
return
}
box.listFiles(&fis, &ret)
return
}
func (box *Box) listFiles(files, ret *[]*FileInfo) {
for _, file := range *files {
if file.isdir {View on GitHub (pinned to 251596fc0d)
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.
Example fix
// before
if err := box.Remove(path); err != nil { return err }
// after: treat already-gone as success
if err := box.Remove(path); err != nil {
if errors.Is(err, os.ErrNotExist) { return nil }
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// Tolerate 'not exist' as the desired end state.
if !box.Exist(path) { return nil } Try / catch
// Treat already-gone as success; recurse for non-empty dirs.
if err := box.Remove(path); err != nil {
if errors.Is(err, os.ErrNotExist) { return nil }
return err
} Prevention
- Empty a directory before removing it (os.Remove does not recurse).
- Close editors/sync clients holding the file on Windows before removing.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- can not remove [%s] caused by it is not a dir
- prepare box conf [%s] failed: %w
- mkdir box conf dir failed: %w
- write box conf [%s] failed: %w
- Create notebook [%s] folder [%s] failed: %s
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/2f3dfb53ad96ecde.
Report an issue: GitHub.