{"record":{"id":"7b4b032c92d84052","repo":"siyuan-note/siyuan","slug":"the-current-kernel-is-in-read-only-mode-storage-r","errorCode":null,"errorMessage":"The current kernel is in read-only mode, storage.remove is not allowed","messagePattern":"The current kernel is in read-only mode, storage\\.remove is not allowed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_storage.go","lineNumber":324,"sourceCode":"\t// siyuan.storage.remove(path) -> Promise<void>\n\tlo.Must0(storage.Set(\"remove\", rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) goja.Value {\n\t\tpromise, resolve, reject := rt.NewPromise()\n\n\t\tvar argErr error\n\t\tvar path string\n\t\tif len(call.Arguments) < 1 {\n\t\t\targErr = fmt.Errorf(\"path required\")\n\t\t} else {\n\t\t\tpath = call.Argument(0).String()\n\t\t}\n\n\t\trunErr := p.worker.Run(func(rt *goja.Runtime) (result any, err error) {\n\t\t\tif argErr != nil {\n\t\t\t\terr = argErr\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif util.ReadOnly {\n\t\t\t\terr = fmt.Errorf(\"The current kernel is in read-only mode, storage.remove is not allowed\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tabs, resolveErr := resolvePath(path)\n\t\t\tif resolveErr != nil {\n\t\t\t\terr = resolveErr\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif abs == p.storageDir {\n\t\t\t\terr = fmt.Errorf(\"cannot remove storage root\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tgo func() (result any, err error) {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\terr = fmt.Errorf(\"panic during siyuan.storage.remove: %v\", r)\n\t\t\t\t\t}","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/api_storage.go#L306-L342","documentation":"Thrown by siyuan.storage.remove() when util.ReadOnly is true. The kernel can boot in read-only mode to forbid all mutations; in that mode deletions are rejected before path resolution or filesystem access. The check runs after the argument-count guard but before the storage-root guard.","triggerScenarios":"Calling storage.remove on a read-only kernel instance (published/preview server, demo environment, workspace mounted read-only). Every remove call rejects regardless of the target path.","commonSituations":"Plugin works on a normal kernel but is later deployed on a publishing server that runs read-only; user booted the kernel with a read-only flag for safety.","solutions":["Detect read-only mode at startup and hide or disable delete features.","Wrap remove in try/catch and degrade gracefully when the rejection indicates read-only mode.","Inform the user that the operation requires a writable kernel."],"exampleFix":"// before\nawait siyuan.storage.remove(path);\n// after\ntry {\n  await siyuan.storage.remove(path);\n} catch (e) {\n  if (String(e).includes('read-only mode')) {\n    // skip deletion silently or notify the user\n  } else {\n    throw e;\n  }\n}","handlingStrategy":"try-catch","validationCode":"// Reuse the read-only probe from error 1012 and skip remove when readOnly is true.","typeGuard":null,"tryCatchPattern":"try {\n  await siyuan.storage.remove(path);\n} catch (e) {\n  if (/read-only mode/.test(String(e))) { /* skip deletion */ return; }\n  throw e;\n}","preventionTips":["Detect read-only mode at startup and disable delete features.","Do not queue deletions on publishing servers."],"tags":["plugin-api","storage","read-only","environment","permissions"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}