NousResearch/hermes-agent · error · Error

Not a directory: ${watchDir}

Error message

Not a directory: ${watchDir}

What it means

Thrown by watchDirectory, the fs.watch-based watcher that replaced the disk-plugin door's 5s readdir poll. It requires the target to be an existing directory — fs.existsSync is false, or statSync shows a non-directory (a file was passed). Used for the plugins directory churn signal; the renderer reconciles on watcher ticks.

Source

Thrown at apps/desktop/electron/main.ts:4964

  return true
}

function closePreviewWatchers() {
  for (const id of previewWatchers.keys()) {
    stopPreviewFileWatch(id)
  }
}

/** Watch a DIRECTORY for entry churn (folders appearing/vanishing) — the
 *  disk-plugin door's "new plugin folder" signal, replacing the renderer's 5s
 *  readdir poll. Same registry + change channel as the preview file watchers
 *  (the renderer reconciles on any tick; per-file edits stay on their own
 *  watches), so stopPreviewFileWatch/closePreviewWatchers manage these too. */
function watchDirectory(rawDir) {
  const watchDir = path.resolve(String(rawDir || ''))

  if (!fs.existsSync(watchDir) || !fs.statSync(watchDir).isDirectory()) {
    throw new Error(`Not a directory: ${watchDir}`)
  }

  const id = crypto.randomBytes(12).toString('base64url')
  let timer = null

  const watcher = fs.watch(watchDir, () => {
    if (timer) {
      clearTimeout(timer)
    }

    timer = setTimeout(() => {
      timer = null
      sendPreviewFileChanged({ id, path: watchDir, url: pathToFileURL(watchDir).toString() })
    }, PREVIEW_WATCH_DEBOUNCE_MS)
  })

  previewWatchers.set(id, {
    close: () => {

View on GitHub (pinned to c896c09c42)

Solutions

  1. Create the directory first: fs.mkdirSync(watchDir, { recursive: true }) before starting the watch.
  2. Pass the plugins directory path, not a file inside it.
  3. Re-request the watch when the directory is (re)created instead of caching a failure.

Example fix

// before
watchDirectory(pluginDir) // throws if dir absent

// after
if (!fs.existsSync(pluginDir)) fs.mkdirSync(pluginDir, { recursive: true })
watchDirectory(pluginDir)
Defensive patterns

Strategy: validation

Validate before calling

const fs = require('fs')
function ensureWatchableDir(dir) {
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
  return fs.statSync(dir).isDirectory()
}

Type guard

function isExistingDirectory(p) {
  try {
    return fs.statSync(p).isDirectory()
  } catch {
    return false
  }
}

Prevention

When it happens

Trigger: IPC watch request for a plugins directory that doesn't exist yet (fresh install, plugins never created); the path points at a file; the directory was deleted between the renderer listing it and the watch call.

Common situations: First run before ~/.hermes/plugins is created; a plugins folder removed while the disk-plugin door was open; passing a file path by mistake.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/8825db8f16ab1ff0. Report an issue: GitHub.