{"record":{"id":"97991fbac6c26fda","repo":"mckaywrigley/chatbot-ui","slug":"error-downloading-assistant-image","errorCode":null,"errorMessage":"Error downloading assistant image","messagePattern":"Error downloading assistant image","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db/storage/assistant-images.ts","lineNumber":49,"sourceCode":"    .upload(filePath, image, {\n      upsert: true\n    })\n\n  if (error) {\n    throw new Error(\"Error uploading image\")\n  }\n\n  return filePath\n}\n\nexport const getAssistantImageFromStorage = async (filePath: string) => {\n  try {\n    const { data, error } = await supabase.storage\n      .from(\"assistant_images\")\n      .createSignedUrl(filePath, 60 * 60 * 24) // 24hrs\n\n    if (error) {\n      throw new Error(\"Error downloading assistant image\")\n    }\n\n    return data.signedUrl\n  } catch (error) {\n    console.error(error)\n  }\n}\n","sourceCodeStart":31,"sourceCodeEnd":57,"githubUrl":"https://github.com/mckaywrigley/chatbot-ui/blob/81328b61d2a4ab597a7a057be70e785cf756d9f8/db/storage/assistant-images.ts#L31-L57","documentation":"Thrown by getAssistantImageFromStorage when createSignedUrl(filePath, 86400) on the assistant_images bucket returns an error. Signed-url creation fails server-side when the object at filePath doesn't exist or the caller can't SELECT it. Note the surrounding try/catch swallows the error and returns undefined, so callers see a missing URL rather than the throw.","triggerScenarios":"Calling getAssistantImageFromStorage with a path that was deleted (e.g. after re-upload created a new timestamped path), an empty-string path, a path from another project/environment, or when no storage.objects SELECT policy covers the object for the current role.","commonSituations":"Rendering an avatar with a stale assistant.image_path saved in the DB; environments (local vs prod) pointing at different buckets; private bucket with no SELECT policy for authenticated users; the swallow-catch making debugging hard because only console.error fires.","solutions":["Check the console output first — the catch logs the real StorageError with its message ('Object not found' vs 'row-level security').","Confirm assistant.image_path actually exists in the bucket via the Supabase dashboard storage browser.","Add a storage.objects SELECT policy on assistant_images for authenticated users (or the object owner) so signed URLs can be issued.","Re-throw or return null explicitly from the catch so callers can distinguish 'no image' from 'fetch failed'."],"exampleFix":"// before\n} catch (error) {\n  console.error(error)\n}\n\n// after\n} catch (error) {\n  console.error(`Failed to sign URL for ${filePath}:`, error)\n  return null\n}","handlingStrategy":"fallback","validationCode":"if (!filePath || filePath.length === 0) {\n  // nothing to sign; skip the call\n}","typeGuard":"const isSignedUrlResult = (\n  r: unknown\n): r is { signedUrl: string } =>\n  typeof r === \"object\" && r !== null && typeof (r as any).signedUrl === \"string\"","tryCatchPattern":"let url: string | null = null\ntry {\n  url = await getAssistantImageFromStorage(path)\n} catch (e) {\n  console.error(\"signed url failed\", e)\n  url = null // caller renders initials/placeholder\n}","preventionTips":["Expect undefined returns from this function (its catch swallows) and null-check at call sites.","Add a SELECT storage policy so signed URLs can be created for authenticated users.","Don't persist signed URLs — they expire in 24h; always re-sign from the stored path."],"tags":["supabase","storage","signed-url","assistant-images","silent-failure"],"backgroundTag":"supabase-signed-url-failed","analyzedSha":"81328b61d2a4ab597a7a057be70e785cf756d9f8","analyzedAt":"2026-08-27T19:24:42.689Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}