siyuan-note/siyuan · error

failed to add storage path to watcher

Error message

failed to add storage path to watcher: %v

What it means

siyuan.storage.watcher.add registers a path with the kernel's filesystem watcher via p.addStorageWatch. When the underlying watcher refuses the path (already watched, does not exist, OS limit reached), the promise rejects with 'failed to add storage path to watcher: %v' wrapping the cause.

Solutions

  1. Read the wrapped cause to distinguish not-exists vs already-watched vs limit-exceeded
  2. Ensure the path exists inside plugin storage before calling watcher.add
  3. Deduplicate: track which paths are already watched and skip re-adding
  4. Raise the OS watch limit (e.g. fs.inotify.max_user_watches on Linux) if exhausted
  5. Create the target directory first (storage.set a placeholder or mkdir) then add the watch

Example fix

// before
await siyuan.storage.watcher.add('logs/') // dir may not exist
// after
await siyuan.storage.set('logs/.keep', '')
if (!watched.has('logs/')) {
  watched.add('logs/')
  await siyuan.storage.watcher.add('logs/')
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (typeof path !== 'string' || !path) throw new TypeError('watcher path required')

Try / catch

try { await siyuan.storage.watcher.add(rel) } catch (e) { if (String(e.message).includes('failed to add storage path to watcher')) { /* ensure path exists / skip duplicates / raise OS watch limit */ } else throw e }

Prevention

When it happens

Trigger: Calling storage.watcher.add('relpath') where the resolved absolute path cannot be added: directory missing, path already being watched, inotify/FSEvents watch limit exhausted, or permission problems.

Common situations: Watching a file that was deleted or not yet created; adding the same path twice across plugin reloads; long-running kernels hitting the OS inotify watch limit; restricted filesystems that disallow watches.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/73e1ae11e367436c. Report an issue: GitHub.

Appendix: 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 9f775e8a12)