From 1d05124b0902373867847716970877e0d23c78c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Jan 2026 15:16:32 +0000 Subject: [PATCH 1/2] fix: add 404 pages for missing repos Add custom not-found pages to handle cases where users navigate to repos that don't exist. The repo 404 shows a link to check GitHub directly. --- app/[owner]/[repo]/not-found.tsx | 48 ++++++++++++++++++++++++++++++++ app/not-found.tsx | 21 ++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 app/[owner]/[repo]/not-found.tsx create mode 100644 app/not-found.tsx diff --git a/app/[owner]/[repo]/not-found.tsx b/app/[owner]/[repo]/not-found.tsx new file mode 100644 index 0000000..823b20c --- /dev/null +++ b/app/[owner]/[repo]/not-found.tsx @@ -0,0 +1,48 @@ +"use client" + +import { ExternalLinkIcon } from "lucide-react" +import Link from "next/link" +import { usePathname } from "next/navigation" +import { Container } from "@/components/container" +import { Title } from "@/components/typography" + +export default function RepoNotFound() { + const pathname = usePathname() + const parts = pathname.split("/").filter(Boolean) + const owner = parts[0] + const repo = parts[1] + + const githubUrl = + owner && repo ? `https://github.com/${owner}/${repo}` : "https://github.com" + + return ( + +
+ 404 — Repository Not Found +

+ We couldn't find a repository at{" "} + + {owner}/{repo} + + . +

+

+ Does this repo exist on GitHub? + + Check on GitHub + + +

+

+ + Go back home + +

+
+
+ ) +} diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..bedc231 --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,21 @@ +import Link from "next/link" +import { Container } from "@/components/container" +import { Title } from "@/components/typography" + +export default function NotFound() { + return ( + +
+ 404 — Page Not Found +

+ The page you're looking for doesn't exist. +

+

+ + Go back home + +

+
+
+ ) +} From 6611581eb1148868cf1e17c4c028dbab8c019ec8 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 13 Jan 2026 14:43:43 +0000 Subject: [PATCH 2/2] fix: move notFound() outside cache boundaries notFound() doesn't work correctly inside "use cache" functions in Next.js 16. Restructured all pages to check for existence before the cache boundary. Also added custom not-found pages for posts and categories. --- app/[owner]/[repo]/[postNumber]/not-found.tsx | 38 ++++++++++++++++ app/[owner]/[repo]/[postNumber]/page.tsx | 29 +++++++++--- .../category/[categorySlug]/not-found.tsx | 39 ++++++++++++++++ .../[repo]/category/[categorySlug]/page.tsx | 44 ++++++++++++++----- app/[owner]/[repo]/page.tsx | 29 ++++++++---- 5 files changed, 152 insertions(+), 27 deletions(-) create mode 100644 app/[owner]/[repo]/[postNumber]/not-found.tsx create mode 100644 app/[owner]/[repo]/category/[categorySlug]/not-found.tsx diff --git a/app/[owner]/[repo]/[postNumber]/not-found.tsx b/app/[owner]/[repo]/[postNumber]/not-found.tsx new file mode 100644 index 0000000..095de18 --- /dev/null +++ b/app/[owner]/[repo]/[postNumber]/not-found.tsx @@ -0,0 +1,38 @@ +"use client" + +import Link from "next/link" +import { usePathname } from "next/navigation" +import { Container } from "@/components/container" +import { Title } from "@/components/typography" + +export default function PostNotFound() { + const pathname = usePathname() + const parts = pathname.split("/").filter(Boolean) + const owner = parts[0] + const repo = parts[1] + + const repoUrl = owner && repo ? `/${owner}/${repo}` : "/" + + return ( + +
+ 404 — Post Not Found +

+ This post doesn't exist in{" "} + + {owner}/{repo} + + . +

+

+ + View all posts in {owner}/{repo} + + + Go home + +

+
+
+ ) +} diff --git a/app/[owner]/[repo]/[postNumber]/page.tsx b/app/[owner]/[repo]/[postNumber]/page.tsx index 29caf1e..8450d34 100644 --- a/app/[owner]/[repo]/[postNumber]/page.tsx +++ b/app/[owner]/[repo]/[postNumber]/page.tsx @@ -121,8 +121,6 @@ export default async function PostPage({ }: { params: Promise<{ owner: string; repo: string; postNumber: string }> }) { - "use cache" - const { postNumber: postNumberStr, owner, repo } = await params if (postNumberStr.endsWith(".md") || postNumberStr.endsWith(".txt")) { @@ -136,6 +134,26 @@ export default async function PostPage({ notFound() } + // Check post exists before cache boundary + const post = await getPostByNumber(owner, repo, postNumber) + if (!post) { + notFound() + } + + return +} + +async function PostPageContent({ + owner, + repo, + postNumber, +}: { + owner: string + repo: string + postNumber: number +}) { + "use cache" + const [ postWithCategory, allLlmUsers, @@ -224,11 +242,8 @@ export default async function PostPage({ .where(and(eq(categories.owner, owner), eq(categories.repo, repo))), ]) - if (!postWithCategory) { - notFound() - } - - const { category, gitContexts, ...post } = postWithCategory + // Post existence already verified before cache boundary + const { category, gitContexts, ...post } = postWithCategory! const gitContext = gitContexts?.[0] ?? null cacheTag(`post:${post.id}`) diff --git a/app/[owner]/[repo]/category/[categorySlug]/not-found.tsx b/app/[owner]/[repo]/category/[categorySlug]/not-found.tsx new file mode 100644 index 0000000..f403bbc --- /dev/null +++ b/app/[owner]/[repo]/category/[categorySlug]/not-found.tsx @@ -0,0 +1,39 @@ +"use client" + +import { ExternalLinkIcon } from "lucide-react" +import Link from "next/link" +import { usePathname } from "next/navigation" +import { Container } from "@/components/container" +import { Title } from "@/components/typography" + +export default function CategoryNotFound() { + const pathname = usePathname() + const parts = pathname.split("/").filter(Boolean) + const owner = parts[0] + const repo = parts[1] + + const repoUrl = owner && repo ? `/${owner}/${repo}` : "/" + + return ( + +
+ 404 — Category Not Found +

+ This category doesn't exist in{" "} + + {owner}/{repo} + + . +

+

+ + View all posts in {owner}/{repo} + + + Go home + +

+
+
+ ) +} diff --git a/app/[owner]/[repo]/category/[categorySlug]/page.tsx b/app/[owner]/[repo]/category/[categorySlug]/page.tsx index 70680fc..791bc7b 100644 --- a/app/[owner]/[repo]/category/[categorySlug]/page.tsx +++ b/app/[owner]/[repo]/category/[categorySlug]/page.tsx @@ -65,26 +65,45 @@ export default async function CategoryPage({ }: { params: Promise<{ owner: string; repo: string; categorySlug: string }> }) { - "use cache" - const { owner, repo, categorySlug } = await params - cacheTag(`category:${categorySlug}`) - const [category, allLlmUsers, repoData] = await Promise.all([ + // Check existence before cache boundary + const [category, repoData] = await Promise.all([ getCategoryBySlug(owner, repo, categorySlug), - getModelsForPicker(), getGithubRepo(owner, repo), ]) - if (!category) { - return notFound() + if (!category || !repoData) { + notFound() } - if (!repoData) { - return notFound() - } + return ( + + ) +} + +async function CategoryPageContent({ + owner, + repo, + categorySlug, + category, +}: { + owner: string + repo: string + categorySlug: string + category: NonNullable>> +}) { + "use cache" + cacheTag(`category:${categorySlug}`) - const categoryPosts = await db + const [allLlmUsers, categoryPosts] = await Promise.all([ + getModelsForPicker(), + db .select({ id: posts.id, number: posts.number, @@ -111,7 +130,8 @@ export default async function CategoryPage({ eq(posts.categoryId, category.id) ) ) - .orderBy(desc(posts.createdAt)) + .orderBy(desc(posts.createdAt)), + ]) const categoriesById = { [category.id]: category } diff --git a/app/[owner]/[repo]/page.tsx b/app/[owner]/[repo]/page.tsx index 56f3774..d084da3 100644 --- a/app/[owner]/[repo]/page.tsx +++ b/app/[owner]/[repo]/page.tsx @@ -58,12 +58,30 @@ export default async function RepoPage({ }: { params: Promise<{ owner: string; repo: string }> }) { - "use cache" - const { owner, repo } = await params + + // Check repo exists before cache boundary + const repoData = await getGithubRepo(owner, repo) + if (!repoData) { + notFound() + } + + return +} + +async function RepoPageContent({ + owner, + repo, + repoData, +}: { + owner: string + repo: string + repoData: NonNullable>> +}) { + "use cache" cacheTag(`repo:${owner}:${repo}`) - const [repoPosts, repoCategories, allLlmUsers, repoData] = await Promise.all([ + const [repoPosts, repoCategories, allLlmUsers] = await Promise.all([ db .select({ id: posts.id, @@ -91,13 +109,8 @@ export default async function RepoPage({ .from(categories) .where(and(eq(categories.owner, owner), eq(categories.repo, repo))), getModelsForPicker(), - getGithubRepo(owner, repo), ]) - if (!repoData) { - return notFound() - } - const categoriesById = Object.fromEntries( repoCategories.map((c) => [c.id, c]) )