diff --git a/src/app/(app)/memories/page.tsx b/src/app/(app)/memories/page.tsx
index 4a4675f..03ba0f9 100644
--- a/src/app/(app)/memories/page.tsx
+++ b/src/app/(app)/memories/page.tsx
@@ -563,9 +563,9 @@ function MemoryTile({ memory, folder, onClick }: { memory: Memory; folder: Folde
⤢
{memory.processingStatus === "processing" && (
-
- Processing…
-
+
+ ⏳
+
)}
{memory.isPrivate && 🔒}
diff --git a/src/app/api/img/route.ts b/src/app/api/img/route.ts
index 6999d01..32b2986 100644
--- a/src/app/api/img/route.ts
+++ b/src/app/api/img/route.ts
@@ -1,7 +1,7 @@
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { NextRequest, NextResponse } from "next/server";
-const ALLOWED_PREFIXES = ["avatars/", "profiles/", "memories/", "thumbnails/"];
+const ALLOWED_PREFIXES = ["avatars/", "profiles/", "memories/", "thumbnails/", "families/"];
export async function GET(req: NextRequest) {
const key = req.nextUrl.searchParams.get("key");
diff --git a/src/app/api/memories/[id]/confirm/route.ts b/src/app/api/memories/[id]/confirm/route.ts
index 2262b04..3989c25 100644
--- a/src/app/api/memories/[id]/confirm/route.ts
+++ b/src/app/api/memories/[id]/confirm/route.ts
@@ -84,10 +84,12 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
}
}
- // Mark as processing and start the media pipeline
+ // Mark as processing and start the media pipeline.
+ // thumbnail failure is non-fatal — always proceed to vision, which marks 'ready'.
await sql`UPDATE memories SET processing_status = 'processing', updated_at = now() WHERE id = ${id}`;
generateThumbnail(id)
+ .catch(e => console.error(`[thumbnail] id=${id}`, e)) // swallow so vision always runs
.then(() => processMemoryVision(id))
.catch(e => console.error(`[memory pipeline] id=${id}`, e));
diff --git a/src/lib/ai/vision.ts b/src/lib/ai/vision.ts
index b36547e..4576015 100644
--- a/src/lib/ai/vision.ts
+++ b/src/lib/ai/vision.ts
@@ -125,7 +125,9 @@ export async function processMemoryVision(memoryId: string): Promise {
`;
} catch (e) {
console.error(`[vision] Failed for memory ${memoryId}:`, e);
- await sql`UPDATE memories SET processing_status = 'failed', updated_at = now() WHERE id = ${memoryId}`;
+ // Vision is optional — mark as ready so the image is still visible.
+ // Only the confirm step (HeadObject failure) should mark as 'failed'.
+ await sql`UPDATE memories SET processing_status = 'ready', updated_at = now() WHERE id = ${memoryId}`;
await logAudit({
action: "vision_processing_failed",
metadata: { memoryId, error: String(e) },