{"record":{"id":"bc3539286b36c937","repo":"NousResearch/hermes-agent","slug":"parent-directory-does-not-exist","errorCode":null,"errorMessage":"Parent directory does not exist","messagePattern":"Parent directory does not exist","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/desktop/electron/main.ts","lineNumber":11805,"sourceCode":"// 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})\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","sourceCodeStart":11787,"sourceCodeEnd":11823,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/main.ts#L11787-L11823","documentation":"writeText never creates directory trees — by design it only writes a file whose parent directory already exists. After resolving the path through resolveRequestedPathForIpc (allowed-roots check), it verifies directoryExists(dirname(resolved)) and throws this error if the parent is missing. The companion error to 'Invalid path' and 'Content too large', it prevents the IPC from being used to materialize arbitrary directory structures.","triggerScenarios":"Writing to a path whose parent directory has not been created yet — e.g. <project>/.hermes/notes.md before .hermes/ exists, or a project directory that was trashed/renamed since the UI captured the path.","commonSituations":"Project folder deleted or moved after the settings UI captured the default project dir; a fresh checkout where an expected subdirectory is gitignored or absent; race between project creation and the first writeText.","solutions":["Create the parent directory first (via the project-creation flow or hermes:setting:defaultProjectDir:set which mkdirs), then retry the write","Verify the captured project path still exists before writing — re-resolve it if the project was moved","Do not rely on writeText to bootstrap a directory tree; it intentionally refuses"],"exampleFix":"// before\nawait ipc.invoke('hermes:fs:writeText', `${projectDir}/notes/IDEA.md`, text)\n\n// after\nawait ipc.invoke('hermes:setting:defaultProjectDir:set', `${projectDir}/notes`) // or otherwise mkdir\nawait ipc.invoke('hermes:fs:writeText', `${projectDir}/notes/IDEA.md`, text)","handlingStrategy":"validation","validationCode":"const parent = path.dirname(filePath)\nif (!existsSync(parent)) { await ensureDirectory(parent) // via a mkdir-capable flow\n}\nawait ipc.invoke('hermes:fs:writeText', filePath, content)","typeGuard":"function parentDirExists(p: string): boolean { return existsSync(path.dirname(p)) }","tryCatchPattern":"catch (e) { if (e instanceof Error && e.message === 'Parent directory does not exist') { await createParentThenRetry() } else throw e }","preventionTips":["Create directories during project creation, not lazily at first write","Re-resolve project paths after the project dir is moved or trashed","Remember writeText intentionally cannot mkdir — plan the directory lifecycle elsewhere"],"tags":["filesystem","validation","ipc","desktop"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}