{"record":{"id":"57b727b9296b01c1","repo":"NousResearch/hermes-agent","slug":"c-couldnotpreview-attachment-label","errorCode":null,"errorMessage":"c.couldNotPreview(attachment.label)","messagePattern":"c\\.couldNotPreview\\(attachment\\.label\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/desktop/src/app/chat/composer/attachments.tsx","lineNumber":98,"sourceCode":"\n    const rawTarget =\n      attachment.path ||\n      attachment.detail ||\n      attachment.refText?.replace(/^@(file|image|url):/, '') ||\n      attachment.label ||\n      ''\n\n    const target = rawTarget.replace(/^`|`$/g, '')\n\n    if (!target) {\n      return\n    }\n\n    try {\n      const preview = await normalizeOrLocalPreviewTarget(target, cwd || undefined)\n\n      if (!preview) {\n        throw new Error(c.couldNotPreview(attachment.label))\n      }\n\n      openPreview(preview, 'manual')\n    } catch (error) {\n      notifyError(error, c.previewUnavailable)\n    }\n  }\n\n  return (\n    <>\n      <Tip label={attachment.path || attachment.detail || attachment.label}>\n        <div className=\"group/attachment relative min-w-0 shrink-0\">\n          <button\n            aria-busy={isUploading || undefined}\n            aria-label={canPreview ? c.previewLabel(attachment.label) : attachment.label}\n            className={cn(\n              'flex max-w-56 items-center gap-2 rounded-2xl border bg-background/50 px-2 py-1.5 text-left shadow-[inset_0_1px_0_rgba(255,255,255,0.18)] transition-colors disabled:cursor-default',\n              hasUploadError","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/src/app/chat/composer/attachments.tsx#L80-L116","documentation":"Thrown in the desktop chat composer's attachment preview handler (attachments.tsx). After the user asks to preview an attachment target (a path or URL extracted from the attachment), normalizeOrLocalPreviewTarget resolves it against the session cwd; if resolution yields nothing usable (no local file, no reachable URL), the code throws a localized 'could not preview <label>' error instead of silently doing nothing. The surrounding catch shows an error notification, so this is a user-facing 'preview target unresolvable' signal, not a crash.","triggerScenarios":"Clicking preview/open on an attachment whose target is a remote path from another machine (SSH-backed session), a path that no longer exists (file deleted or cwd changed), a bare filename that normalizeOrLocalPreviewTarget cannot anchor to the supplied cwd, or a URL scheme the preview normalizer rejects.","commonSituations":"Desktop app connected to a remote backend over URL+token where attachment paths refer to the backend's filesystem, not the local one; sessions resumed after the working directory moved; attachment metadata produced by an older schema where path/detail fields are empty.","solutions":["Verify the attachment path exists locally (or on the machine the preview resolver runs on) and that the session cwd is correct.","If the backend is remote, mount/sync the backend filesystem or use a target the resolver supports (absolute local path or http(s) URL).","For developers: check normalizeOrLocalPreviewTarget's accepted schemes and make the attachment carry an absolute path or explicit cwd.","Update a stale attachment payload — re-attach the file instead of previewing a dangling reference."],"exampleFix":"// before\nconst preview = await normalizeOrLocalPreviewTarget(target, cwd || undefined)\nif (!preview) {\n  throw new Error(c.couldNotPreview(attachment.label))\n}\n\n// after — give the resolver a real anchor and surface why it failed\nconst preview = await normalizeOrLocalPreviewTarget(\n  attachment.path?.startsWith('/') ? attachment.path : joinPath(cwd, target),\n  cwd || undefined\n)\nif (!preview) {\n  throw new Error(c.couldNotPreview(attachment.path || attachment.label))\n}","handlingStrategy":"try-catch","validationCode":"const target = rawTarget.replace(/^`|`$/g, '')\nconst isPreviewable = target && (target.startsWith('/') || target.startsWith('http'))\nif (!isPreviewable) return  // don't attempt preview for unanchorable targets","typeGuard":"const isPreviewableTarget = (t: string | undefined): t is string =>\n  Boolean(t) && (/^https?:\\/\\//.test(t!) || t!.startsWith('/') || t!.startsWith('~'))","tryCatchPattern":"try {\n  const preview = await normalizeOrLocalPreviewTarget(target, cwd || undefined)\n  if (!preview) throw new Error(c.couldNotPreview(attachment.label))\n  openPreview(preview, 'manual')\n} catch (error) {\n  // surfaced as toast, not a crash — keep it that way\n  notifyError(error, c.previewUnavailable)\n}","preventionTips":["Carry absolute paths (or explicit cwd) in attachment metadata","For remote backends, only expose preview for client-reachable targets","Feature-detect the preview action in the UI instead of relying on the thrown error"],"tags":["desktop","preview","attachments","filesystem"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}