diff --git a/src/components/MarketplaceComments.test.tsx b/src/components/MarketplaceComments.test.tsx
new file mode 100644
index 00000000..30dc4f95
--- /dev/null
+++ b/src/components/MarketplaceComments.test.tsx
@@ -0,0 +1,49 @@
+import { cleanup, render, screen } from "@testing-library/react";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { DirectoryComments } from "@/components/directory/DirectoryComments";
+import { McpComments } from "@/components/mcp/McpComments";
+import { PromptComments } from "@/components/prompts/PromptComments";
+import { SkillComments } from "@/components/skills/SkillComments";
+
+const surfaces = [
+ {
+ name: "directory",
+ render: () => ,
+ },
+ {
+ name: "MCP",
+ render: () => ,
+ },
+ {
+ name: "prompt",
+ render: () => ,
+ },
+ {
+ name: "skill",
+ render: () => ,
+ },
+];
+
+describe("marketplace comment loading errors", () => {
+ beforeEach(() => {
+ vi.stubGlobal("fetch", vi.fn());
+ });
+
+ afterEach(() => {
+ cleanup();
+ vi.unstubAllGlobals();
+ });
+
+ it.each(surfaces)("does not report an empty $name thread when loading fails", async ({ render: renderSurface }) => {
+ vi.mocked(fetch).mockResolvedValue(
+ new Response(JSON.stringify({ error: "Comments unavailable" }), { status: 503 })
+ );
+
+ render(renderSurface());
+
+ expect(await screen.findByRole("alert")).toHaveTextContent("Comments unavailable");
+ expect(
+ screen.queryByText("No comments yet. Be the first to share your thoughts!")
+ ).not.toBeInTheDocument();
+ });
+});
diff --git a/src/components/directory/DirectoryComments.tsx b/src/components/directory/DirectoryComments.tsx
index dfd6b74b..9abaee41 100644
--- a/src/components/directory/DirectoryComments.tsx
+++ b/src/components/directory/DirectoryComments.tsx
@@ -44,15 +44,20 @@ export function DirectoryComments({ listingId, isAuthenticated }: DirectoryComme
async function fetchComments() {
try {
const res = await fetch(`/api/directory/${listingId}/comments`);
- if (res.ok) {
- const data = await res.json();
- setComments(data.comments || []);
- setTotal(data.total || 0);
+ if (!res.ok) {
+ const data = await res.json().catch(() => ({}));
+ setError(data.error || "Failed to load comments");
+ return;
}
+
+ const data = await res.json();
+ setComments(data.comments || []);
+ setTotal(data.total || 0);
} catch {
- // silently fail
+ setError("Failed to load comments");
+ } finally {
+ setLoading(false);
}
- setLoading(false);
}
async function handlePost(parentId: string | null = null) {
@@ -213,7 +218,7 @@ export function DirectoryComments({ listingId, isAuthenticated }: DirectoryComme
)}
{error && (
-
+
{error}
)}
@@ -222,7 +227,7 @@ export function DirectoryComments({ listingId, isAuthenticated }: DirectoryComme
- ) : comments.length > 0 ? (
+ ) : error ? null : comments.length > 0 ? (
{comments.map((comment) => renderComment(comment))}
diff --git a/src/components/mcp/McpComments.tsx b/src/components/mcp/McpComments.tsx
index ca2053d6..5b551dba 100644
--- a/src/components/mcp/McpComments.tsx
+++ b/src/components/mcp/McpComments.tsx
@@ -44,15 +44,20 @@ export function McpComments({ slug, isAuthenticated }: McpCommentsProps) {
async function fetchComments() {
try {
const res = await fetch(`/api/mcp/${slug}/comments`);
- if (res.ok) {
- const data = await res.json();
- setComments(data.comments || []);
- setTotal(data.total || 0);
+ if (!res.ok) {
+ const data = await res.json().catch(() => ({}));
+ setError(data.error || "Failed to load comments");
+ return;
}
+
+ const data = await res.json();
+ setComments(data.comments || []);
+ setTotal(data.total || 0);
} catch {
- // silently fail
+ setError("Failed to load comments");
+ } finally {
+ setLoading(false);
}
- setLoading(false);
}
async function handlePost(parentId: string | null = null) {
@@ -212,7 +217,7 @@ export function McpComments({ slug, isAuthenticated }: McpCommentsProps) {
)}
{error && (
-
+
{error}
)}
@@ -221,7 +226,7 @@ export function McpComments({ slug, isAuthenticated }: McpCommentsProps) {
- ) : comments.length > 0 ? (
+ ) : error ? null : comments.length > 0 ? (
{comments.map((comment) => renderComment(comment))}
diff --git a/src/components/prompts/PromptComments.tsx b/src/components/prompts/PromptComments.tsx
index 1d91a471..832bf297 100644
--- a/src/components/prompts/PromptComments.tsx
+++ b/src/components/prompts/PromptComments.tsx
@@ -44,15 +44,20 @@ export function PromptComments({ slug, isAuthenticated }: PromptCommentsProps) {
async function fetchComments() {
try {
const res = await fetch(`/api/prompts/${slug}/comments`);
- if (res.ok) {
- const data = await res.json();
- setComments(data.comments || []);
- setTotal(data.total || 0);
+ if (!res.ok) {
+ const data = await res.json().catch(() => ({}));
+ setError(data.error || "Failed to load comments");
+ return;
}
+
+ const data = await res.json();
+ setComments(data.comments || []);
+ setTotal(data.total || 0);
} catch {
- // silently fail
+ setError("Failed to load comments");
+ } finally {
+ setLoading(false);
}
- setLoading(false);
}
async function handlePost(parentId: string | null = null) {
@@ -212,7 +217,7 @@ export function PromptComments({ slug, isAuthenticated }: PromptCommentsProps) {
)}
{error && (
-
+
{error}
)}
@@ -221,7 +226,7 @@ export function PromptComments({ slug, isAuthenticated }: PromptCommentsProps) {
- ) : comments.length > 0 ? (
+ ) : error ? null : comments.length > 0 ? (
{comments.map((comment) => renderComment(comment))}
diff --git a/src/components/skills/SkillComments.tsx b/src/components/skills/SkillComments.tsx
index 9f09bb38..f06a40a8 100644
--- a/src/components/skills/SkillComments.tsx
+++ b/src/components/skills/SkillComments.tsx
@@ -44,15 +44,20 @@ export function SkillComments({ slug, isAuthenticated }: SkillCommentsProps) {
async function fetchComments() {
try {
const res = await fetch(`/api/skills/${slug}/comments`);
- if (res.ok) {
- const data = await res.json();
- setComments(data.comments || []);
- setTotal(data.total || 0);
+ if (!res.ok) {
+ const data = await res.json().catch(() => ({}));
+ setError(data.error || "Failed to load comments");
+ return;
}
+
+ const data = await res.json();
+ setComments(data.comments || []);
+ setTotal(data.total || 0);
} catch {
- // silently fail
+ setError("Failed to load comments");
+ } finally {
+ setLoading(false);
}
- setLoading(false);
}
async function handlePost(parentId: string | null = null) {
@@ -213,7 +218,7 @@ export function SkillComments({ slug, isAuthenticated }: SkillCommentsProps) {
)}
{error && (
-
+
{error}
)}
@@ -222,7 +227,7 @@ export function SkillComments({ slug, isAuthenticated }: SkillCommentsProps) {
- ) : comments.length > 0 ? (
+ ) : error ? null : comments.length > 0 ? (
{comments.map((comment) => renderComment(comment))}