NousResearch/hermes-agent · warning · Error

Invalid rename

Error message

Invalid rename

What it means

The 'hermes:fs:rename' IPC renames an item within its parent directory. Before touching the filesystem it validates inputs: src and the new base name must be non-empty, the name must not be '.', '..', or contain '/' or '\\'. Any violation throws 'Invalid rename' — the design guarantees a rename can never traverse out of its parent dir or become a move.

Source

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

ipcMain.handle('hermes:fs:desktopPluginsRoot', async () => localPluginsRoot('desktop-plugins'))

// The LOCAL agent-plugin root (`<HERMES_HOME>/plugins`), same Electron-local
// resolution as above. This is the desktop half of a UNIFIED plugin package:
// an agent plugin may ship `desktop/plugin.js` alongside its Python code (the
// same shape as `dashboard/manifest.json`), and the renderer's disk door scans
// this root for it — one installable folder serving both SDKs.
ipcMain.handle('hermes:fs:agentPluginsRoot', async () => localPluginsRoot('plugins'))

// Rename a file/folder in place. The renderer passes the existing path + a new
// base name; the destination is resolved in the SAME parent dir so a rename can
// never move the item elsewhere or traverse out. Rejects on a name collision.
ipcMain.handle('hermes:fs:rename', async (_event, targetPath, newName) => {
  const src = String(targetPath || '').trim()
  const name = String(newName || '').trim()

  if (!src || !name || name === '.' || name === '..' || name.includes('/') || name.includes('\\')) {
    throw new Error('Invalid rename')
  }

  const dst = path.join(path.dirname(src), name)

  if (dst === src) {
    return { path: dst }
  }

  if (fs.existsSync(dst)) {
    throw new Error(`"${name}" already exists`)
  }

  await fs.promises.rename(src, dst)

  return { path: dst }
})

// Write a small UTF-8 text file (e.g. a project's IDEA.md at creation). The path

View on GitHub (pinned to c896c09c42)

Solutions

  1. Trim and validate the new name client-side: non-empty, no '/' or '\\', not '.' or '..' before invoking the IPC
  2. Show inline validation in the rename UI instead of letting the IPC rejection surface
  3. If a move to another directory is the actual goal, use a different flow — this API intentionally cannot do it

Example fix

// before
await ipc.invoke('hermes:fs:rename', path, newName)

// after
const name = newName.trim()
if (!name || name === '.' || name === '..' || /[\\/]/.test(name)) return setRenameError('Enter a plain file name')
await ipc.invoke('hermes:fs:rename', path, name)
Defensive patterns

Strategy: validation

Validate before calling

function isValidRename(name: string): boolean {
  const n = name.trim()
  return !!n && n !== '.' && n !== '..' && !n.includes('/') && !n.includes('\\')
}
if (!isValidRename(newName)) { setRenameError('Name must be a plain base name'); return }
await ipc.invoke('hermes:fs:rename', path, newName)

Type guard

function isValidNewBaseName(name: unknown): name is string {
  return typeof name === 'string' && name.trim().length > 0 && name.trim() !== '.' && name.trim() !== '..' && !/[\\/]/.test(name.trim())
}

Try / catch

catch (e) { if (e instanceof Error && e.message === 'Invalid rename') setRenameError('Use a plain name without slashes') else throw e }

Prevention

When it happens

Trigger: Renderer passes an empty path or empty new name, or a new name containing path separators (e.g. 'sub/dir', '..\\evil'), '.', or '..' — typically from an unsanitized inline-rename input.

Common situations: Rename field submitted while empty; user types a slash intending a nested move; copy-paste of a full path into the rename box.

Related errors


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