{"record":{"id":"0ba9ad4ce1d7b2d2","repo":"siyuan-note/siyuan","slug":"can-not-remove-s-caused-by-it-is-a-reserved-fil","errorCode":null,"errorMessage":"can not remove [%s] caused by it is a reserved file","messagePattern":"can not remove \\[(.+?)\\] caused by it is a reserved file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/mount.go","lineNumber":278,"sourceCode":"\tboundAVIDs, err := sql.QueryBoundBlockAVIDsInBox(nil, rootIDs, boxID)\n\tif nil != err {\n\t\treturn nil, err\n\t}\n\treturn groupDeletedAttributeViewBlocks(boundAVIDs), nil\n}\n\nfunc RemoveBox(boxID string) (err error) {\n\tif !ast.IsNodeIDPattern(boxID) {\n\t\treturn errors.New(\"invalid notebook ID\")\n\t}\n\tif _, loaded := boxLock.LoadOrStore(boxID, true); loaded {\n\t\terr = errors.New(Conf.language(239))\n\t\treturn\n\t}\n\tdefer boxLock.Delete(boxID)\n\n\tif util.IsReservedFilename(boxID) {\n\t\treturn fmt.Errorf(\"can not remove [%s] caused by it is a reserved file\", boxID)\n\t}\n\n\tFlushTxQueue()\n\tsql.FlushQueue()\n\t// 索引和笔记本目录删除后无法再读取 custom-avs，需提前收集；实际删除成功后再清理绑定行。\n\tdeletedAttrViewBlockIDs, err := collectBoxDeletedAttributeViewBlocks(boxID)\n\tif nil != err {\n\t\treturn fmt.Errorf(\"query database-bound blocks in notebook [%s] failed: %w\", boxID, err)\n\t}\n\tisUserGuide := IsUserGuide(boxID)\n\tlocalPath := filepath.Join(util.DataDir, boxID)\n\tif !filelock.IsExist(localPath) {\n\t\tremoveHPathRefreshBox(boxID)\n\t\tforgetRuntimeNormalBox(boxID)\n\t\tremoveMasterPasswordMigrationBox(boxID)\n\t\treturn\n\t}\n\tif !gulu.File.IsDir(localPath) {","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/mount.go#L260-L296","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: removing every data subdir as a notebook\nfor _, dir := range dataDirs {\n    removeNotebook(dir.Name())\n}\n// after: skip reserved names\nfor _, dir := range dataDirs {\n    if util.IsReservedFilename(dir.Name()) {\n        continue\n    }\n    removeNotebook(dir.Name())\n}","handlingStrategy":"validation","validationCode":"// mirrors util.IsReservedFilename; apply before calling removeNotebook\nvar reservedNames = map[string]bool{\"assets\": true, \"conf\": true, \"export\": true, \"history\": true, \"snapshots\": true, \"temp\": true}\nfunction isRemovableNotebook(id string) bool {\n    return isValidNotebookID(id) && !reservedNames[id]\n}","typeGuard":null,"tryCatchPattern":"if err := removeNotebook(boxID); err != nil {\n    if strings.Contains(err.Error(), \"reserved file\") {\n        // permanent refusal — do not retry; fix the target list instead\n        return fmt.Errorf(\"skip protected directory %s\", boxID)\n    }\n    return err\n}","preventionTips":["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"],"tags":["validation","notebook","protected-path","api"],"backgroundTag":"protected-path-blocked","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}