{"record":{"id":"3683caabff11ba87","repo":"NousResearch/hermes-agent","slug":"invalid-delete","errorCode":null,"errorMessage":"Invalid delete","messagePattern":"Invalid delete","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/desktop/electron/main.ts","lineNumber":11819,"sourceCode":"\n  const resolved = resolveRequestedPathForIpc(expandUserPath(raw), { purpose: 'Write text file' })\n\n  if (!directoryExists(path.dirname(resolved))) {\n    throw new Error('Parent directory does not exist')\n  }\n\n  await fs.promises.writeFile(resolved, text, 'utf8')\n\n  return { path: resolved }\n})\n\n// Move a file/folder to the OS trash (recoverable) — the VS Code \"Delete\"\n// default. `shell.trashItem` routes to Finder/Explorer/Files trash per platform.\nipcMain.handle('hermes:fs:trash', async (_event, targetPath) => {\n  const target = String(targetPath || '').trim()\n\n  if (!target) {\n    throw new Error('Invalid delete')\n  }\n\n  await shell.trashItem(target)\n\n  return true\n})\n\n// Git-driven worktree management (\"Start work\" flow). Errors surface to the\n// renderer as rejected promises so it can toast a friendly message.\nipcMain.handle('hermes:git:worktreeList', async (_event, repoPath) => listWorktrees(repoPath, resolveGitBinary()))\n\nipcMain.handle('hermes:git:worktreeAdd', async (_event, repoPath, options) =>\n  addWorktree(repoPath, options || {}, resolveGitBinary())\n)\n\nipcMain.handle('hermes:git:worktreeRemove', async (_event, repoPath, worktreePath, options) =>\n  removeWorktree(repoPath, worktreePath, options || {}, resolveGitBinary())\n)","sourceCodeStart":11801,"sourceCodeEnd":11837,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/main.ts#L11801-L11837","documentation":"The 'hermes:fs:trash' IPC moves a file/folder to the OS trash via shell.trashItem. Its only input validation is a non-empty trimmed target path; an empty/undefined value throws 'Invalid delete' before the shell call. Failures from trashItem itself surface as different (shell) errors.","triggerScenarios":"Renderer invokes hermes:fs:trash with '', '   ', null, or undefined — typically a delete action fired from UI state where the selected item's path is not set (nothing selected, or selection cleared by a re-render).","commonSituations":"Delete button enabled with an empty selection; keyboard shortcut handled before the focused-item state resolves; list virtualization returning null item for the row index.","solutions":["Guard the delete action on a non-empty selected item path in the renderer","Disable the delete affordance when no item is selected","Distinguish this validation error from real trashItem failures (permissions, missing file) which throw different messages"],"exampleFix":"// before\nonDelete={() => void ipc.invoke('hermes:fs:trash', selected?.path)}\n\n// after\nonDelete={() => { if (selected?.path) void ipc.invoke('hermes:fs:trash', selected.path) }}","handlingStrategy":"validation","validationCode":"const target = typeof targetPath === 'string' ? targetPath.trim() : ''\nif (!target) { notify('Select an item to delete'); return }\nawait ipc.invoke('hermes:fs:trash', target)","typeGuard":"function hasTrashTarget(v: unknown): v is string { return typeof v === 'string' && v.trim().length > 0 }","tryCatchPattern":"catch (e) { if (e instanceof Error && e.message === 'Invalid delete') notify('Nothing selected to delete') else throw e }","preventionTips":["Disable delete actions when the selection is empty","Resolve the focused item's path before enabling keyboard delete shortcuts"],"tags":["validation","filesystem","ipc","desktop","trash"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}