{"record":{"id":"df71352aa7450d17","repo":"NousResearch/hermes-agent","slug":"could-not-write-artifact-file","errorCode":null,"errorMessage":"Could not write artifact file","messagePattern":"Could not write artifact file","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/desktop/src/app/chat/right-rail/preview-artifact.tsx","lineNumber":60,"sourceCode":"    '</body></html>'\n  ].join('\\n')\n}\n\n/** Write the composed document to a real temp file through the existing\n *  buffer-save IPC, then hand it to the OS browser. A blob/data URL can't\n *  cross into the OS default browser, so a file on disk is the honest path. */\nasync function openHtmlInBrowser(content: string): Promise<void> {\n  const bridge = window.hermesDesktop\n\n  if (!bridge?.saveImageBuffer || !bridge.openExternal) {\n    throw new Error('Desktop bridge unavailable')\n  }\n\n  const bytes = new TextEncoder().encode(composeArtifactHtml(content))\n  const path = await bridge.saveImageBuffer(bytes, '.html')\n\n  if (!path) {\n    throw new Error('Could not write artifact file')\n  }\n\n  const fileUrl = `file://${path.startsWith('/') ? '' : '/'}${path.replace(/\\\\/g, '/')}`\n\n  if (bridge.openPreviewInBrowser) {\n    await bridge.openPreviewInBrowser(fileUrl)\n\n    return\n  }\n\n  await bridge.openExternal(fileUrl)\n}\n\n/**\n * Live view for renderable artifact content.\n *\n * HTML runs in an `<iframe sandbox=\"allow-scripts\">` — scripts execute in an\n * opaque origin with no same-origin access, no top navigation, no popups, no","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/src/app/chat/right-rail/preview-artifact.tsx#L42-L78","documentation":"Thrown by openHtmlInBrowser in the desktop artifact preview after calling bridge.saveImageBuffer(bytes, '.html') and receiving a falsy return. The Electron main-side handler writes the buffer to a temp file and returns its path; an empty/undefined path means the write failed or the handler rejected softly (e.g. temp dir not writable, IPC handler error swallowed, disk full). The renderer treats 'no path' as 'artifact file could not be created' and refuses to build a file:// URL from nothing.","triggerScenarios":"Invoking 'open artifact in browser' when the OS temp directory is not writable or full, when the saveImageBuffer IPC handler threw on the main side and resolved undefined, or when a sandboxed renderer passes a buffer the main process cannot serialize.","commonSituations":"Disk-full or TMPDIR pointing to a read-only location inside dev containers; main-process exception in the temp-file writer after an Electron upgrade changed dialog/fs APIs; permission restrictions on macOS with hardened runtime denying temp writes.","solutions":["Check that the OS temp directory is writable and has free space (echo $TMPDIR; df -h /tmp).","Inspect the Electron main process log for an exception in the saveImageBuffer IPC handler and fix that root cause.","Restart/reinstall the desktop app if the preload/main bundle versions drifted.","As a developer: make the main-side handler return an error message instead of undefined so the renderer can show the real cause."],"exampleFix":"// before\nconst path = await bridge.saveImageBuffer(bytes, '.html')\nif (!path) {\n  throw new Error('Could not write artifact file')\n}\n\n// after (main-side handler surfaces the failure)\nconst path = await bridge.saveImageBuffer(bytes, '.html')\nif (!path) {\n  throw new Error('Could not write artifact file (check temp dir permissions and free space)')\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let path: string\ntry {\n  path = await bridge.saveImageBuffer(bytes, '.html')\n} catch (ipcError) {\n  throw new Error(`Artifact write IPC failed: ${String(ipcError)}`)\n}\nif (!path) {\n  throw new Error('Could not write artifact file (temp dir unwritable or full)')\n}","preventionTips":["Monitor free space in TMPDIR on user machines","Make the main-side saveImageBuffer handler return {path} or {error} explicitly instead of undefined","Distinguish IPC rejection (catch) from soft undefined return (falsy check) in diagnostics"],"tags":["desktop","electron","ipc","temp-files","filesystem"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}