siyuan-note/siyuan · error

failed to add storage path to watcher: %v

Error message

failed to add storage path to watcher: %v

What it means

Thrown when p.addStorageWatch(abs) returns an error after the path was successfully resolved. The kernel registers the absolute storage path with its underlying file watcher (fsnotify-based); if registration fails the promise rejects with the wrapped underlying error in place of %v. Common underlying causes: the path does not exist yet, permission denied, OS watcher/inotify limits exceeded, or the path is a file whose parent cannot be watched.

Source

Thrown at kernel/plugin/api_storage.go:73

		var path string
		if len(call.Arguments) >= 1 && goja.IsString(call.Argument(0)) {
			path = call.Argument(0).String()
		} else {
			argErr = fmt.Errorf("path required")
		}

		runErr := p.worker.Run(func(rt *goja.Runtime) (result any, err error) {
			if argErr != nil {
				err = argErr
				return
			}
			abs, resolveErr := resolvePath(path)
			if resolveErr != nil {
				err = resolveErr
				return
			}
			if addErr := p.addStorageWatch(abs); addErr != nil {
				err = fmt.Errorf("failed to add storage path to watcher: %v", addErr)
			}
			return
		}, func(rt *goja.Runtime, result any, err error) {
			if lo.IsNil(err) {
				if resolveErr := resolve(result); resolveErr != nil {
					logging.LogErrorf("[plugin:%s] siyuan.storage.watcher.add resolve: %v", p.Name, resolveErr)
				}
			} else {
				if rejectErr := reject(rt.NewGoError(err)); rejectErr != nil {
					logging.LogErrorf("[plugin:%s] siyuan.storage.watcher.add reject: %v", p.Name, rejectErr)
				}
			}
		})
		if runErr != nil {
			logging.LogErrorf("[plugin:%s] siyuan.storage.watcher.add worker run: %v", p.Name, runErr)
			if rejectErr := reject(rt.NewGoError(runErr)); rejectErr != nil {
				logging.LogErrorf("[plugin:%s] siyuan.storage.watcher.add reject: %v", p.Name, rejectErr)
			}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Ensure the file or directory exists (create it via storage.put) before calling watcher.add.
  2. Raise the OS watcher limit (e.g. echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf on Linux).
  3. Check file permissions on the storage dir and its parents.
  4. Watch the parent directory instead of a non-existent leaf when appropriate.

Example fix

// before
await siyuan.storage.watcher.add('cache/state.json');
// after
await siyuan.storage.put('cache/state.json', '{}');
await siyuan.storage.watcher.add('cache/state.json');
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure target exists before watching:
try { await siyuan.storage.get(path); } catch { await siyuan.storage.put(path, ''); }

Try / catch

try {
  await siyuan.storage.watcher.add(path);
} catch (e) {
  console.warn('watcher.add failed:', String(e));
  // optionally retry once after ensuring the file exists
}

Prevention

When it happens

Trigger: watcher.add('subdir/file.json') where 'subdir/file.json' does not exist on disk; on Linux hitting fs.inotify.max_user_watches; on Windows a path held open exclusively by another process; watching a network mount the watcher cannot monitor.

Common situations: Plugin tries to watch a path before creating it (call put first, then add); deployment on a restricted container with low inotify limits; symlinked storage dir on a filesystem fsnotify does not support.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/73e1ae11e367436c. Report an issue: GitHub.