{"record":{"id":"de89eb6a09a76e42","repo":"m1k1o/neko","slug":"unable-to-start-file-transfer-dir-watcher-w","errorCode":null,"errorMessage":"unable to start file transfer dir watcher: %w","messagePattern":"unable to start file transfer dir watcher: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/plugins/filetransfer/manager.go","lineNumber":148,"sourceCode":"func (m *Manager) Start() error {\n\t// send init message once a user connects\n\tm.sessions.OnConnected(func(session types.Session) {\n\t\tm.sendUpdate(session)\n\t})\n\n\t// if file transfer is disabled, return immediately without starting the watcher\n\tif !m.config.Enabled {\n\t\treturn nil\n\t}\n\n\tif _, err := os.Stat(m.config.RootDir); os.IsNotExist(err) {\n\t\terr = os.Mkdir(m.config.RootDir, os.ModePerm)\n\t\tm.logger.Err(err).Msg(\"creating file transfer directory\")\n\t}\n\n\twatcher, err := fsnotify.NewWatcher()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to start file transfer dir watcher: %w\", err)\n\t}\n\n\tgo func() {\n\t\tdefer watcher.Close()\n\n\t\t// periodically refresh file list\n\t\tticker := time.NewTicker(m.config.RefreshInterval)\n\t\tdefer ticker.Stop()\n\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-m.shutdown:\n\t\t\t\tm.logger.Info().Msg(\"shutting down file transfer manager\")\n\t\t\t\treturn\n\t\t\tcase <-ticker.C:\n\t\t\t\terr, changed := m.refresh()\n\t\t\t\tif err != nil {\n\t\t\t\t\tm.logger.Err(err).Msg(\"unable to refresh file transfer list\")","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/plugins/filetransfer/manager.go#L130-L166","documentation":"Returned by filetransfer Manager.Start when fsnotify.NewWatcher() itself fails, so the directory watcher that keeps the file-transfer listing fresh cannot even be created. This is an OS/resource-level failure, not a problem with the configured directory.","triggerScenarios":"Manager.Start runs and fsnotify.NewWatcher() returns an error — typically fd/inotify watch limits exhausted (EMFILE/ENOSPC), or running in a container/kernel without inotify support.","commonSituations":"Long-running servers hitting fs.inotify.max_user_watches / max_user_instances limits; minimal containers or sandboxes without inotify; low file-descriptor ulimits.","solutions":["Check and raise inotify limits: sysctl fs.inotify.max_user_watches and fs.inotify.max_user_instances (e.g. to 524288 / 512)","Raise the process file-descriptor limit (ulimit -n / systemd LimitNOFILE)","Verify the kernel/container supports inotify; in restricted containers run with a host-matching /proc/sys/fs/inotify","Retry Start after freeing resources; if it persists, capture the wrapped %w error for the exact errno"],"exampleFix":"// before\n$ ulimit -n   # 256\n\n// after\n$ ulimit -n 65536\n$ sudo sysctl -w fs.inotify.max_user_watches=524288","handlingStrategy":"fallback","validationCode":"// preflight check for inotify availability\nif _, err := os.Stat(\"/proc/sys/fs/inotify/max_user_watches\"); err != nil {\n    log.Warn().Msg(\"inotify unavailable; file listing may be stale\")\n}\nif n := readInt(\"/proc/sys/fs/inotify/max_user_instances\"); n > 0 && n <= usedInstances() {\n    return fmt.Errorf(\"inotify instance limit reached (%d)\", n)\n}","typeGuard":null,"tryCatchPattern":"if err := m.Start(); err != nil {\n    if strings.Contains(err.Error(), \"unable to start file transfer dir watcher\") {\n        log.Error().Err(err).Msg(\"increase fs.inotify limits or fd ulimit\")\n        // degrade: run with periodic polling instead of a watcher\n    }\n    return err\n}","preventionTips":["Raise fs.inotify.max_user_watches and max_user_instances on hosts running many watchers","Set LimitNOFILE high in the systemd unit for the server","Test plugin Start inside the actual container image — minimal kernels may lack inotify"],"tags":["go","fsnotify","inotify","file-watcher","resources"],"backgroundTag":"inotify-watch-limit-exceeded","analyzedSha":"b0f01cedea68893e85a3fd852c0521238c285695","analyzedAt":"2026-09-01T10:35:56.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}