{"record":{"id":"f24e3c384bfe4e75","repo":"NousResearch/hermes-agent","slug":"invalid-path","errorCode":null,"errorMessage":"Invalid path","messagePattern":"Invalid path","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/desktop/electron/main.ts","lineNumber":11793,"sourceCode":"\n  if (fs.existsSync(dst)) {\n    throw new Error(`\"${name}\" already exists`)\n  }\n\n  await fs.promises.rename(src, dst)\n\n  return { path: dst }\n})\n\n// Write a small UTF-8 text file (e.g. a project's IDEA.md at creation). The path\n// is hardened (resolveRequestedPathForIpc) and the parent must already exist —\n// this never creates directory trees or escapes the allowed roots, and content\n// is size-capped so it can't be abused as a bulk-write primitive.\nipcMain.handle('hermes:fs:writeText', async (_event, filePath, content) => {\n  const raw = String(filePath || '').trim()\n\n  if (!raw) {\n    throw new Error('Invalid path')\n  }\n\n  const text = String(content ?? '')\n\n  if (text.length > 1_000_000) {\n    throw new Error('Content too large')\n  }\n\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})","sourceCodeStart":11775,"sourceCodeEnd":11811,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/main.ts#L11775-L11811","documentation":"The 'hermes:fs:writeText' IPC writes a small UTF-8 text file (e.g. a project's IDEA.md). Its first validation requires a non-empty trimmed filePath; an empty/whitespace/undefined path throws 'Invalid path' before any filesystem access. Subsequent guards (size cap, allowed roots via resolveRequestedPathForIpc, parent-exists) run after this one.","triggerScenarios":"Renderer invokes writeText with '', '   ', null, or undefined as filePath — usually an uninitialized state value or a dialog that was cancelled before producing a path.","commonSituations":"Saving a file whose path comes from a prompt that returned null on cancel; template code running before the path state is populated.","solutions":["Ensure the caller has a concrete file path before invoking — guard on a truthy trimmed string","If the path originates from a save/pick dialog, bail when the dialog is cancelled","Remember the path is user-~ expanded and root-checked, so pass a normal absolute or ~-relative path, not empty"],"exampleFix":"// before\nawait ipc.invoke('hermes:fs:writeText', filePath, text)\n\n// after\nconst p = filePath?.trim()\nif (!p) return\nawait ipc.invoke('hermes:fs:writeText', p, text)","handlingStrategy":"validation","validationCode":"const p = typeof filePath === 'string' ? filePath.trim() : ''\nif (!p) { notify('Choose a file path first'); return }\nawait ipc.invoke('hermes:fs:writeText', p, content)","typeGuard":"function isNonEmptyPath(v: unknown): v is string { return typeof v === 'string' && v.trim().length > 0 }","tryCatchPattern":"catch (e) { if (e instanceof Error && e.message === 'Invalid path') notify('File path is required') else throw e }","preventionTips":["Bail on cancelled save/pick dialogs before calling writeText","Keep file-path state non-null in the component contract (make it required in props)"],"tags":["validation","filesystem","ipc","desktop"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}