{"record":{"id":"93a5093983a3d0a8","repo":"NousResearch/hermes-agent","slug":"missing-url","errorCode":null,"errorMessage":"Missing URL","messagePattern":"Missing URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/main.ts","lineNumber":4674,"sourceCode":"    .then(value => usableTitle((value || '').slice(0, 240)))\n    .then(\n      async value => value || usableTitle(((await fetchHtmlTitleWithRenderer(url).catch(() => '')) || '').slice(0, 240))\n    )\n    .then(clean => {\n      cacheTitle(key, clean)\n      titleInflight.delete(key)\n\n      return clean\n    })\n\n  titleInflight.set(key, pending)\n\n  return pending\n}\n\nasync function resourceBufferFromUrl(rawUrl) {\n  if (!rawUrl) {\n    throw new Error('Missing URL')\n  }\n\n  if (rawUrl.startsWith('data:')) {\n    const match = rawUrl.match(/^data:([^;,]+)?(;base64)?,(.*)$/s)\n\n    if (!match) {\n      throw new Error('Invalid data URL')\n    }\n\n    const mimeType = match[1] || 'application/octet-stream'\n    const encoded = match[3] || ''\n    const buffer = match[2] ? Buffer.from(encoded, 'base64') : Buffer.from(decodeURIComponent(encoded), 'utf8')\n\n    return { buffer, mimeType }\n  }\n\n  if (/^file:/i.test(rawUrl)) {\n    const { resolvedPath } = await resolveReadableFileForIpc(rawUrl, { purpose: 'Image file' })","sourceCodeStart":4656,"sourceCodeEnd":4692,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/main.ts#L4656-L4692","documentation":"The cheapest guard in resourceBufferFromUrl: the rawUrl argument is falsy. The function is the shared loader behind copyImageFromUrl / saveImageFromUrl and any other resource fetch IPC — an empty/undefined URL never reaches the data:/file:/http branches.","triggerScenarios":"Renderer IPC calling copyImageFromUrl('') or saveImageFromUrl(undefined); a context-menu handler firing with no image src (e.g. the user right-clicked a non-image element and src extraction returned empty).","commonSituations":"Context menus enabled on containers; a URL field cleared before submit; nullish coalescing bugs upstream that turn null into ''.","solutions":["Pass a non-empty URL string (data:, file:, or http(s):).","In the renderer, only enable the copy/save-image menu items when an src was actually extracted from the click target.","Coerce with a default or bail early: if (!url) return."],"exampleFix":"// before\nmenu.items.push({ label: 'Copy image', click: () => ipc.send('copy-image', imgSrc) })\n\n// after\nif (imgSrc) {\n  menu.items.push({ label: 'Copy image', click: () => ipc.send('copy-image', imgSrc) })\n}","handlingStrategy":"validation","validationCode":"function hasUrl(u) {\n  return typeof u === 'string' && u.trim() !== ''\n}\nif (!hasUrl(rawUrl)) return // never call the IPC with an empty URL","typeGuard":"function isNonEmptyUrl(u) {\n  return typeof u === 'string' && u.length > 0\n}","tryCatchPattern":null,"preventionTips":["Only show copy/save-image menu items when an src exists","Bail early on empty src extraction","Type the IPC boundary so undefined can't sneak through"],"tags":["ipc","validation","url","image","desktop"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}