Skip to content

Conversation

@julianbenegas
Copy link
Member

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.

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.
@vercel
Copy link
Contributor

vercel bot commented Jan 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
forums Error Error Jan 13, 2026 2:44pm

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.
Comment on lines +245 to +246
// Post existence already verified before cache boundary
const { category, gitContexts, ...post } = postWithCategory!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Post existence already verified before cache boundary
const { category, gitContexts, ...post } = postWithCategory!
// Defensive check: post should exist (verified before cache boundary),
// but we add this check to handle race conditions or data inconsistencies.
// If the post is not found, we notFound() even though we're in a cached function,
// as this is a data integrity issue that should be handled gracefully.
if (!postWithCategory) {
notFound()
}
const { category, gitContexts, ...post } = postWithCategory

Non-null assertion on postWithCategory! without null check could crash with TypeError if post is deleted between queries or data becomes inconsistent

View Details
📝 Patch Details
diff --git a/app/[owner]/[repo]/[postNumber]/page.tsx b/app/[owner]/[repo]/[postNumber]/page.tsx
index 8450d34..bd795b2 100644
--- a/app/[owner]/[repo]/[postNumber]/page.tsx
+++ b/app/[owner]/[repo]/[postNumber]/page.tsx
@@ -242,8 +242,15 @@ async function PostPageContent({
       .where(and(eq(categories.owner, owner), eq(categories.repo, repo))),
   ])
 
-  // Post existence already verified before cache boundary
-  const { category, gitContexts, ...post } = postWithCategory!
+  // Defensive check: post should exist (verified before cache boundary),
+  // but we add this check to handle race conditions or data inconsistencies.
+  // If the post is not found, we notFound() even though we're in a cached function,
+  // as this is a data integrity issue that should be handled gracefully.
+  if (!postWithCategory) {
+    notFound()
+  }
+
+  const { category, gitContexts, ...post } = postWithCategory
   const gitContext = gitContexts?.[0] ?? null
 
   cacheTag(`post:${post.id}`)

Analysis

BUG ANALYSIS:

The code on line 245 uses a non-null assertion without a defensive null check:

const { category, gitContexts, ...post } = postWithCategory!

This was changed from a previous version that had an explicit null check (if (!postWithCategory) { notFound() }). The developer's assumption is that because the post exists was verified in the PostPage function before the cache boundary, it will definitely exist in PostPageContent. However, this assumption breaks down for several reasons:

  1. Race Condition Between Queries: The PostPage function checks if the post exists using getPostByNumber(), but then PostPageContent queries the database again independently. Between these two queries, the post could be deleted from the database.

  2. Different Query Structures: The first check uses getPostByNumber() (from lib/data/posts), while the second uses a direct Drizzle query with a LEFT JOIN. These queries could diverge if there's data inconsistency or timing issues.

  3. Unsafe Destructuring: If postWithCategory is undefined at runtime, TypeScript's non-null assertion doesn't prevent a crash. Instead, the destructuring will throw: TypeError: Cannot destructure property 'category' of 'undefined' (reading 'category'). This is worse than a graceful 404.

  4. Edge Cases: While race conditions are rare, they're possible especially in high-concurrency scenarios or if the post is deleted between the initial check and the cache query.

FIX EXPLANATION:

The fix restores the null check that was previously present:

if (!postWithCategory) {
  notFound()
}

const { category, gitContexts, ...post } = postWithCategory

This handles the edge case where the post becomes unavailable due to:

  • Race conditions where post is deleted between queries
  • Data consistency issues between the two query structures
  • Any other unexpected conditions

By calling notFound(), users get a proper 404 page instead of a server error. The check also removes the type-unsafe non-null assertion, making the code more robust.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants