m1k1o/neko · error
unable to watch file transfer dir: %w
Error message
unable to watch file transfer dir: %w
What it means
Returned by Manager.Start when the watcher was created successfully but watcher.Add(m.config.RootDir) fails, i.e. the file-transfer root directory cannot be registered with fsnotify for change events.
Source
Thrown at server/internal/plugins/filetransfer/manager.go:196
m.logger.Debug().Str("event", e.String()).Msg("file transfer dir watcher event")
err, changed := m.refresh()
if err != nil {
m.logger.Err(err).Msg("unable to refresh file transfer list")
}
if changed {
m.broadcastUpdate()
}
}
case err := <-watcher.Errors:
m.logger.Err(err).Msg("error in file transfer dir watcher")
}
}
}()
if err := watcher.Add(m.config.RootDir); err != nil {
return fmt.Errorf("unable to watch file transfer dir: %w", err)
}
// initial refresh
err, changed := m.refresh()
if err != nil {
return fmt.Errorf("unable to refresh file transfer list: %w", err)
}
if changed {
m.broadcastUpdate()
}
return nil
}
func (m *Manager) deleteFileHandler(w http.ResponseWriter, r *http.Request) error {
session, ok := auth.GetSession(r)
if !ok {
return utils.HttpUnauthorized("session not found")View on GitHub (pinned to b0f01cedea)
Solutions
- Verify config.RootDir exists and the server process has read/execute permission on it (ls -ld <dir>); create it manually with correct ownership if needed
- Check the Mkdir error log line ('creating file transfer directory') — if it failed, fix parent-dir permissions
- Raise inotify watch limits (fs.inotify.max_user_watches) if the error is ENOSPC/EMFILE
- Ensure the volume isn't mounted read-only and correct m.config.RootDir in the plugin config if it points to the wrong path
Example fix
// before rootdir: /nonexistent/var/filetransfer // after sudo mkdir -p /var/filetransfer && sudo chown app:app /var/filetransfer # config rootdir: /var/filetransfer
Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(m.config.RootDir)
if err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(m.config.RootDir, os.ModePerm); err != nil { return err }
} else { return err }
} else if !info.IsDir() {
return fmt.Errorf("%s is not a directory", m.config.RootDir)
}
if err := unix.Access(m.config.RootDir, unix.R_OK|unix.X_OK); err != nil {
return fmt.Errorf("no read access to %s: %w", m.config.RootDir, err)
} Try / catch
if err := m.Start(); err != nil {
if strings.Contains(err.Error(), "unable to watch file transfer dir") {
log.Error().Err(err).Str("dir", m.config.RootDir).Msg("fix root dir existence/permissions")
}
return err
} Prevention
- Create RootDir at install time with the server user as owner
- Point config.RootDir at a stable, non-removable, non-RO-mounted path
- Include a startup readiness check that verifies RootDir is writable before Start
When it happens
Trigger: Start runs, NewWatcher succeeds, but watcher.Add on config.RootDir returns an error — most commonly RootDir does not exist or is unreadable (permission denied), or the per-user watch limit is exhausted.
Common situations: RootDir point to a path that was never created (only Mkdir attempted when IsNotExist, which can itself fail silently — only logged), RootDir owned by another user, or a container where the volume is mounted read-only.
Related errors
- session login disabled
- unable to start file transfer dir watcher: %w
- unable to refresh file transfer list: %w
- image data not found
- screencast not enabled
AI-assisted analysis of m1k1o/neko@b0f01cedea (2026-09-01).
Data as JSON: /api/errors/0650d8f44ae96bb9.
Report an issue: GitHub.