siyuan-note/siyuan · error
The current kernel is in read-only mode, storage.put is not
Error message
The current kernel is in read-only mode, storage.put is not allowed
What it means
Thrown by siyuan.storage.put() when util.ReadOnly is true. The kernel can run in read-only mode (e.g. a published or preview instance where writes are forbidden); in that mode all storage mutations are rejected before any filesystem access. The check runs after argument validation but before path resolution and writing.
Source
Thrown at kernel/plugin/api_storage.go:247
lo.Must0(storage.Set("put", rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) goja.Value {
promise, resolve, reject := rt.NewPromise()
var argErr error
var path, content string
if len(call.Arguments) < 2 {
argErr = fmt.Errorf("path and content required")
} else {
path = call.Argument(0).String()
content = call.Argument(1).String()
}
runErr := p.worker.Run(func(rt *goja.Runtime) (result any, err error) {
if argErr != nil {
err = argErr
return
}
if util.ReadOnly {
err = fmt.Errorf("The current kernel is in read-only mode, storage.put is not allowed")
return
}
abs, resolveErr := resolvePath(path)
if resolveErr != nil {
err = resolveErr
return
}
go func() (result any, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic during siyuan.storage.put: %v", r)
}
p.worker.Run(func(rt *goja.Runtime) (_ any, _ error) {
if lo.IsNil(err) {
if resolveErr := resolve(result); resolveErr != nil {View on GitHub (pinned to 251596fc0d)
Solutions
- Detect read-only mode at plugin startup and disable write features or switch to in-memory state.
- Wrap put in try/catch and degrade gracefully when writes are rejected.
- Surface a user-facing notice that the feature requires a writable kernel.
Example fix
// before
await siyuan.storage.put('state.json', body);
// after
try {
await siyuan.storage.put('state.json', body);
} catch (e) {
if (String(e).includes('read-only mode')) {
notifyUser('This plugin needs a writable kernel.');
} else {
throw e;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Detect read-only mode once at startup by probing a write:
let readOnly = false;
try { await siyuan.storage.put('__probe', '1'); await siyuan.storage.remove('__probe'); }
catch (e) { readOnly = /read-only mode/.test(String(e)); } Try / catch
try {
await siyuan.storage.put(path, body);
} catch (e) {
if (/read-only mode/.test(String(e))) {
notifyUser('Write features require a writable kernel.');
return;
}
throw e;
} Prevention
- Probe writable mode at startup and disable write-only features up front.
- Keep an in-memory fallback for state when running on publishing servers.
When it happens
Trigger: Calling storage.put on a kernel started with the read-only flag, or on a hosted/published SiYuan instance that enforces util.ReadOnly. Any put call will reject regardless of path validity.
Common situations: Plugin developed against a normal desktop kernel is later loaded on a read-only publishing server; CI/demo environments booted read-only to prevent mutation; the user launched the kernel with a read-only workspace flag.
Related errors
- The current kernel is in read-only mode, storage.remove is n
- failed to add storage path to watcher: %v
- failed to make directory: %w
- failed to write file: %w
- siyuan.storage: path traversal not allowed
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/26f88552fca07ee6.
Report an issue: GitHub.