siyuan-note/siyuan · error

initialize fsnotify watcher

Error message

initialize fsnotify watcher: %w

What it means

addStorageWatch lazily creates the plugin's fsnotify watcher. If fsnotify.NewWatcher() fails (e.g. the platform lacks inotify support or the OS exhausted watch resources), the error is wrapped as 'initialize fsnotify watcher: <cause>'.

Solutions

  1. Raise fs.inotify.max_user_instances and fs.inotify.max_user_watches (sysctl) on Linux
  2. Reduce the number of watched paths (watch fewer directories, use recursive logic sparingly)
  3. On mobile/unsupported platforms, avoid file-watch APIs and use polling or explicit refresh instead
  4. Check the wrapped cause to confirm resource exhaustion vs platform limitation

Example fix

// before
await plugin.addStorageWatch(largeDir);

// after
try {
  await plugin.addStorageWatch(largeDir);
} catch (e) {
  console.warn("watch unavailable, falling back to polling:", String(e));
}
Defensive patterns

Strategy: fallback

Try / catch

try {
  await plugin.addStorageWatch(path);
} catch (e) {
  if (String(e).includes("initialize fsnotify watcher")) {
    startPollingFallback(path); // poll mtime instead of fsnotify
  } else throw e;
}

Prevention

When it happens

Trigger: First storage-watch registration on a plugin when no watcher exists yet and the OS cannot create one: inotify instances/watch descriptors exhausted (Linux), unsupported platform (mobile builds disable file watching), or resource limits.

Common situations: Linux systems with low fs.inotify.max_user_instances/max_user_watches; containers with restricted inotify; mobile kernels where watching is disabled; watching very large directory trees exhausting descriptors.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

Thrown at kernel/plugin/plugin.go:781

	}
}

// addStorageWatch adds a path to the fsnotify watcher to watch for storage file/directory changes.
func (p *KernelPlugin) addStorageWatch(path string) (err error) {
	if !isPluginFileWatchSupported() {
		return errPluginFileWatchUnsupported
	}

	p.watcherMu.Lock()
	defer p.watcherMu.Unlock()

	if contextErr := p.context.Err(); contextErr != nil {
		return fmt.Errorf("plugin stopped: %w", contextErr)
	}
	if p.watcher == nil {
		p.watcher, err = fsnotify.NewWatcher()
		if err != nil {
			return fmt.Errorf("initialize fsnotify watcher: %w", err)
		}
		p.watcherDone = make(chan struct{})
		go p.startStorageWatch(p.watcher, p.watcherDone)
	}

	err = p.watcher.Add(path)
	return
}

// removeStorageWatch removes a path from the fsnotify watcher to stop watching for storage file/directory changes.
func (p *KernelPlugin) removeStorageWatch(path string) (err error) {
	if !isPluginFileWatchSupported() {
		return errPluginFileWatchUnsupported
	}

	p.watcherMu.Lock()
	defer p.watcherMu.Unlock()

View on GitHub (pinned to 9f775e8a12)