siyuan-note/siyuan · error
can not remove [ ] caused by it is a reserved file
Error message
can not remove [%s] caused by it is a reserved file
What it means
RemoveBox rejects notebook IDs whose directory name is a reserved filename (util.IsReservedFilename, e.g. 'assets', 'conf', 'export', 'history', 'snapshots', 'temp' style reserved names under the data directory). Deleting such a directory would corrupt the workspace, so it returns "can not remove [%s] caused by it is a reserved file". It is a deliberate protective refusal, not a bug.
Solutions
- Do not attempt to remove reserved directories through the notebook API; delete only real notebooks (valid node-ID directories)
- Filter the target list against reserved names before calling removeNotebook
- If a reserved-named folder was created by mistake, stop the kernel and remove it manually at the filesystem level only if you are certain it is not workspace-critical
Example fix
// before: removing every data subdir as a notebook
for _, dir := range dataDirs {
removeNotebook(dir.Name())
}
// after: skip reserved names
for _, dir := range dataDirs {
if util.IsReservedFilename(dir.Name()) {
continue
}
removeNotebook(dir.Name())
} Defensive patterns
Strategy: validation
Validate before calling
// mirrors util.IsReservedFilename; apply before calling removeNotebook
var reservedNames = map[string]bool{"assets": true, "conf": true, "export": true, "history": true, "snapshots": true, "temp": true}
function isRemovableNotebook(id string) bool {
return isValidNotebookID(id) && !reservedNames[id]
} Try / catch
if err := removeNotebook(boxID); err != nil {
if strings.Contains(err.Error(), "reserved file") {
// permanent refusal — do not retry; fix the target list instead
return fmt.Errorf("skip protected directory %s", boxID)
}
return err
} Prevention
- Enumerate removal targets from lsNotebooks (which returns real notebooks only), not from raw data-directory listings
- Never treat reserved workspace directories (assets, conf, history, etc.) as notebooks
- Do not create notebooks whose directory name collides with reserved workspace folders
When it happens
Trigger: Calling /api/notebook/removeNotebook with boxID equal to a reserved workspace directory name that happens to pass the node-ID check, or a crafted/legacy notebook directory named with a reserved word.
Common situations: Trying to 'remove' pseudo-notebooks created by tooling under reserved names; scripts iterating the data directory and calling removeNotebook on every subdirectory including reserved ones; migrated workspaces containing manually created folders with reserved names.
Related errors
- invalid notebook ID
- 106
- AI editor action must not be empty
- asset path does not belong to a notebook
- block [ ] type is locked: expected , got
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/0ba9ad4ce1d7b2d2.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/mount.go:278
boundAVIDs, err := sql.QueryBoundBlockAVIDsInBox(nil, rootIDs, boxID)
if nil != err {
return nil, err
}
return groupDeletedAttributeViewBlocks(boundAVIDs), nil
}
func RemoveBox(boxID string) (err error) {
if !ast.IsNodeIDPattern(boxID) {
return errors.New("invalid notebook ID")
}
if _, loaded := boxLock.LoadOrStore(boxID, true); loaded {
err = errors.New(Conf.language(239))
return
}
defer boxLock.Delete(boxID)
if util.IsReservedFilename(boxID) {
return fmt.Errorf("can not remove [%s] caused by it is a reserved file", boxID)
}
FlushTxQueue()
sql.FlushQueue()
// 索引和笔记本目录删除后无法再读取 custom-avs,需提前收集;实际删除成功后再清理绑定行。
deletedAttrViewBlockIDs, err := collectBoxDeletedAttributeViewBlocks(boxID)
if nil != err {
return fmt.Errorf("query database-bound blocks in notebook [%s] failed: %w", boxID, err)
}
isUserGuide := IsUserGuide(boxID)
localPath := filepath.Join(util.DataDir, boxID)
if !filelock.IsExist(localPath) {
removeHPathRefreshBox(boxID)
forgetRuntimeNormalBox(boxID)
removeMasterPasswordMigrationBox(boxID)
return
}
if !gulu.File.IsDir(localPath) {View on GitHub (pinned to 9f775e8a12)