{"record":{"id":"db3e157d4c2116f1","repo":"NousResearch/hermes-agent","slug":"could-not-open-preview-target-item-target","errorCode":null,"errorMessage":"Could not open preview target: ${item.target}","messagePattern":"Could not open preview target: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/desktop/src/app/chat/composer/status-stack/preview-row.tsx","lineNumber":33,"sourceCode":"\ninterface PreviewStatusRowProps {\n  item: PreviewArtifact\n  onDismiss: (id: string) => void\n}\n\n/** One detected artifact, single line, always visible: filename + open + close. */\nexport const PreviewStatusRow = memo(function PreviewStatusRow({ item, onDismiss }: PreviewStatusRowProps) {\n  const { t } = useI18n()\n  const openSources = useStore($previewTabSources)\n  const [opening, setOpening] = useState(false)\n  // A tab open IS a pane in the tree now, so its presence is the whole answer.\n  const isOpen = openSources.includes(item.target)\n\n  const resolveTarget = async () => {\n    const target = await normalizeOrLocalPreviewTarget(item.target, item.cwd || undefined)\n\n    if (!target) {\n      throw new Error(`Could not open preview target: ${item.target}`)\n    }\n\n    return target\n  }\n\n  const togglePreview = async () => {\n    if (opening) {\n      return\n    }\n\n    if (isOpen) {\n      closePreviewForSource(item.target)\n\n      return\n    }\n\n    setOpening(true)\n","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/src/app/chat/composer/status-stack/preview-row.tsx#L15-L51","documentation":"Thrown by PreviewStatusRow in the desktop composer's status stack when the user clicks to open a detected artifact and normalizeOrLocalPreviewTarget fails to resolve item.target (with item.cwd) into anything previewable. The throw is caught by the row's toggle flow and typically surfaced as a failed open action; it distinguishes 'target unresolvable' from 'preview pane open failed'.","triggerScenarios":"Clicking the open/toggle control on a preview status row whose item.target is a relative path that cannot be anchored (item.cwd missing or stale), a path to a file that was deleted since detection, or a remote/SSH-only path on a backend the local resolver cannot reach.","commonSituations":"Artifacts detected from terminal output in remote sessions (paths exist only on the backend), build artifacts cleaned between detection and click, cwd-relative targets after the session's working directory changed.","solutions":["Confirm the artifact file still exists at the resolved location; if it was cleaned, re-run the generating command.","Ensure item.cwd is populated so relative targets resolve; if the detector emits bare filenames, make it emit absolute paths.","For remote backends, preview only targets reachable from the client (local mount or http URL).","As a developer: catch this error in togglePreview and show a toast with item.target so users see which artifact failed."],"exampleFix":"// before\nconst resolveTarget = async () => {\n  const target = await normalizeOrLocalPreviewTarget(item.target, item.cwd || undefined)\n  if (!target) {\n    throw new Error(`Could not open preview target: ${item.target}`)\n  }\n  return target\n}\n\n// after — fall back to an absolute-path attempt before giving up\nconst resolveTarget = async () => {\n  const target = (await normalizeOrLocalPreviewTarget(item.target, item.cwd || undefined))\n    ?? (await normalizeOrLocalPreviewTarget(item.absolutePath ?? item.target, undefined))\n  if (!target) {\n    throw new Error(`Could not open preview target: ${item.target}`)\n  }\n  return target\n}","handlingStrategy":"try-catch","validationCode":"if (!item.target || !(item.target.startsWith('/') || /^https?:/.test(item.target) || item.cwd)) {\n  // cannot resolve a bare target without a cwd — mark the row unopenable\n  return null\n}","typeGuard":"interface PreviewItem { target: string; cwd?: string }\nconst isResolvablePreviewItem = (i: PreviewItem): boolean =>\n  i.target.startsWith('/') || /^https?:/.test(i.target) || Boolean(i.cwd)","tryCatchPattern":"const togglePreview = async () => {\n  if (opening) return\n  try {\n    const target = await resolveTarget()\n    openPreview(target, 'status-row')\n  } catch (error) {\n    notifyError(error, `Preview unavailable for ${item.target}`)\n  } finally {\n    setOpening(false)\n  }\n}","preventionTips":["Emit absolute paths from the artifact detector so cwd is irrelevant","Populate item.cwd whenever the target is relative","Disable the open button for targets that failed resolution before (cache failures per source)"],"tags":["desktop","preview","artifacts","filesystem"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}