{"record":{"id":"8eb8030bd01dd939","repo":"siyuan-note/siyuan","slug":"this-operation-is-not-supported-in-read-only-mode-8eb803","errorCode":null,"errorMessage":"This operation is not supported in read-only mode","messagePattern":"This operation is not supported in read-only mode","errorType":"http","errorClass":null,"httpStatus":403,"severity":"warning","filePath":"kernel/server/serve.go","lineNumber":1578,"sourceCode":"\t\t\t// logging.LogDebugf(\"WebDAV [%s %s]\", r.Method, r.URL.String())\n\t\t},\n\t}\n\n\tginGroup := ginServer.Group(\"/webdav\", model.CheckAuth, model.CheckAdminRole)\n\t// ginGroup.Any NOT support extension methods (PROPFIND etc.)\n\tginGroup.Match(WebDavMethods, \"/*path\", func(c *gin.Context) {\n\t\tif util.ReadOnly {\n\t\t\tswitch c.Request.Method {\n\t\t\tcase http.MethodPost,\n\t\t\t\thttp.MethodPut,\n\t\t\t\thttp.MethodDelete,\n\t\t\t\tMethodMkCol,\n\t\t\t\tMethodCopy,\n\t\t\t\tMethodMove,\n\t\t\t\tMethodLock,\n\t\t\t\tMethodUnlock,\n\t\t\t\tMethodPropPatch:\n\t\t\t\tc.AbortWithError(http.StatusForbidden, errors.New(model.Conf.Language(34)))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\thandler.ServeHTTP(c.Writer, c.Request)\n\t})\n}\n\nfunc serveCalDAV(ginServer *gin.Engine) {\n\t// REF: https://github.com/emersion/hydroxide/blob/master/carddav/carddav.go\n\thandler := caldav.Handler{\n\t\tBackend: &model.CalDavBackend{},\n\t\tPrefix:  model.CalDavPrincipalsPath,\n\t}\n\n\tginServer.Match(CalDavMethods, \"/.well-known/caldav\", func(c *gin.Context) {\n\t\t// logging.LogDebugf(\"CalDAV -> [%s] %s\", c.Request.Method, c.Request.URL.String())\n\t\thandler.ServeHTTP(c.Writer, c.Request)\n\t})","sourceCodeStart":1560,"sourceCodeEnd":1596,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/server/serve.go#L1560-L1596","documentation":"Returned (HTTP 403) by the WebDAV route when util.ReadOnly is enabled and the request uses a write/mutating method (POST, PUT, DELETE, MKCOL, COPY, MOVE, LOCK, UNLOCK, PROPPATCH). The message comes from model.Conf.Language(34) — the localized 'read-only mode' string. This is intentional gating, not a bug.","triggerScenarios":"A WebDAV client (Finder, Windows Explorer, Cyberduck) issues PUT/MKCOL/MOVE/etc. against /webdav/* while the kernel is running in read-only mode (--readonly flag or config).","commonSituations":"Kernel started with --readonly for a demo/preview; user mounted the workspace read-only intentionally; config flip forgotten after maintenance; sync/backup drive running read-only.","solutions":["Restart the kernel without --readonly if writes are intended.","If read-only is intentional, configure the WebDAV client to mount read-only / disable sync writes.","Verify the access mode in Settings - About / startup flags and remove the read-only switch."],"exampleFix":"// before\nkernel --serve --readonly\n// after\nkernel --serve","handlingStrategy":"validation","validationCode":"// Client side: probe capabilities before issuing writes.\nasync function canWriteWebDAV(base: string, auth: string): Promise<boolean> {\n  const r = await fetch(`${base}/webdav/`, { method: 'OPTIONS', headers: { Authorization: auth } })\n  // If server advertises read-only or returns 403 on PROPFIND write-class, treat as read-only.\n  return !r.headers.get('allow')?.toUpperCase().includes('READONLY')\n}","typeGuard":null,"tryCatchPattern":"// Handle 403 gracefully and surface a clear message.\ntry { await webdavPut(...) }\ncatch (e) {\n  if (e.status === 403) console.warn('workspace is read-only; write skipped')\n  else throw e\n}","preventionTips":["Do not start the kernel with --readonly unless you intend to block writes.","Mount WebDAV read-only in clients when the workspace is read-only.","Document the read-only flag so operators remember to turn it off after maintenance."],"tags":["webdav","read-only","http-403","server","config"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}