DEV Community

Lavie
Lavie

Posted on

I catalogued every AI hallucination in Next.js 15 — then I built a system to prevent them permanently

TL;DR: AI coding assistants have 5 recurring hallucination patterns that ship critical bugs to production. I documented 47 of them, then wrote 22 .mdc rule files that physically prevent Cursor from generating insecure auth, deprecated imports, and broken Next.js 15 patterns. The entire system is free and open source.


I stopped writing code three months ago.

Not because I was burned out or because I found a "no-code" tool. I stopped because I realized I was playing the wrong game.

I was using Cursor's Agent mode to build a Next.js SaaS app. The code looked clean. The types were correct. Everything compiled. And then I deployed to production and discovered that every single auth check in my application was insecure.

The AI had used getSession() everywhere -- a function that reads a JWT from cookies without verifying it. Any user could craft a fake token and my app would treat them as authenticated.

That bug cost me 6 hours to fix. And it taught me something important.

The Uncomfortable Truth About AI Coding

AI models are crowd-pleasers. They'll generate whatever you ask for, with perfect TypeScript syntax and zero compiler errors. But they have systematic blind spots -- patterns baked into their training data that are now deprecated, insecure, or broken.

I started keeping a spreadsheet. Every time Cursor generated a pattern that I had to manually fix, I logged it. After three months, I had documented 47 recurring hallucination patterns across Next.js 15, Supabase, and Stripe.

Here are the five most dangerous:

1. The getSession() Security Flaw

What the AI generates:

// Looks correct. Compiles fine. Catastrophically insecure.
const { data: { session } } = await supabase.auth.getSession()
if (!session) redirect('/login')
Enter fullscreen mode Exit fullscreen mode

Why it's dangerous: getSession() reads the JWT from cookies but does NOT verify it with Supabase's auth server. An attacker can craft a fake JWT and your server will accept it.

The secure pattern:

// This sends a request to Supabase to cryptographically verify the token
const { data: { user } } = await supabase.auth.getUser()
if (!user) redirect('/login')
Enter fullscreen mode Exit fullscreen mode

The training data is dominated by pre-2024 tutorials when getSession() was the recommended approach. Supabase changed their guidance -- but the models haven't caught up.

2. The Next.js 15 Params Time Bomb

What the AI generates:

export default function Page({ params }: { params: { slug: string } }) {
  return <h1>{params.slug}</h1>  // CRASHES: params is a Promise in Next.js 15
}
Enter fullscreen mode Exit fullscreen mode

The correct pattern:

export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params
  return <h1>{slug}</h1>
}
Enter fullscreen mode Exit fullscreen mode

This breaks silently in development (Next.js does some auto-handling) but explodes in production builds. I've seen 4 different open-source repos with this exact bug.

3. The Deprecated Package Import

What the AI generates:

import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
Enter fullscreen mode Exit fullscreen mode

The correct import:

import { createBrowserClient } from '@supabase/ssr'
Enter fullscreen mode Exit fullscreen mode

auth-helpers-nextjs was deprecated in favor of @supabase/ssr. The old package doesn't handle cookie-based sessions correctly with the App Router. But the AI has seen 10x more examples of the old package.

4. The Client Component Disease

The AI loves 'use client'. Ask it for a product page, and it'll slap 'use client' at the top and use useState + useEffect to fetch data from an API route.

This is the React equivalent of driving a Ferrari in first gear. Server Components fetch data on the server with zero client-side JavaScript. They're faster, more secure, and simpler. But the AI defaults to 2022 patterns because that's what it was trained on.

5. The Missing RLS Time Bomb

When the AI creates a Supabase table, it almost never enables Row Level Security. This means your database is wide open -- any authenticated user can read any other user's data using the anon key.


My Solution: Architecture Rules That Physically Constrain the AI

After documenting all 47 patterns, I realized that "being careful" wasn't enough. I needed guardrails that work automatically.

Cursor supports .mdc rule files -- markdown files with YAML frontmatter that the AI reads based on which files you're editing. They act like a senior code reviewer that's always watching.

Here's how they work:

---
description: Supabase auth security -- enforces getUser() over getSession()
globs: ["**/app/**", "**/actions/**", "**/api/**"]
---

# Supabase Auth Security

SECURITY CRITICAL: NEVER use getSession() in server-side code.
ALWAYS use getUser() -- it verifies the JWT against the auth server.

SECURE: const { data: { user } } = await supabase.auth.getUser()
INSECURE: const { data: { session } } = await supabase.auth.getSession()
Enter fullscreen mode Exit fullscreen mode

When this rule is active, every file matching **/app/** automatically receives this constraint. The AI cannot generate getSession() calls because the rule explicitly overrides its training data.

I wrote 22 of these rules. Not 22 random guidelines -- 22 precisely scoped constraints targeting the most dangerous hallucination patterns across:

  • Authentication security (getUser vs getSession, deprecated packages)
  • Next.js 15 breaking changes (async params, generateMetadata)
  • Component architecture (Server vs Client, minimal 'use client')
  • Database security (RLS enforcement, service_role restrictions)
  • Input validation (Zod at every boundary)
  • Performance (parallel fetching, N+1 prevention, dynamic imports)
  • Payments (server-only Stripe, webhook signature verification)
  • OWASP Top 10 (injection, XSS, rate limiting)

The 3-Stage Agentic Loop

The rules prevent bad output. But to get great output, you need a prompting framework. Here's mine:

Stage 1: Plan (Act as the Architect)

"I need to build a magic-link auth flow. Before writing any code, create an Architectural Plan: which files, which are Server vs Client Components, what RLS policies. Wait for my approval."

Forcing the AI to plan before coding catches bad architectural decisions before they become bad code.

Stage 2: Implement (Constrained Batches)

"Execute Phase 1 only -- database schemas and RLS policies. Stop when Phase 1 is done."

AI output quality degrades with length. Small batches keep it sharp.

Stage 3: Verify (Self-Audit)

"Review the server action you wrote. Did you use getUser()? Did you validate input with Zod? Check against our project rules."

Self-reflection catches 80% of hallucinations before you hit save.


Try It -- It's Free and Open Source

I packaged the entire system as Vibe Stack -- a Next.js 15 + Supabase boilerplate with all 22 rules pre-configured.

What you get:

  • Next.js 15 App Router + React 19 + TypeScript (strict mode)
  • 22 battle-tested .mdc architecture rules
  • Supabase SSR auth (the secure way)
  • 4 pre-configured MCP servers (GitHub, Filesystem, Supabase, Browser)
  • Working auth flow (login, signup, email confirm, protected dashboard)
  • Error boundaries, loading states, custom 404
  • Complete Architecture Decision Records explaining every "why"
git clone https://github.com/vibestackdev/vibe-stack.git
cd vibe-stack && npm install
cp .env.example .env.local
npm run dev
Enter fullscreen mode Exit fullscreen mode

Open it in Cursor. Start building. Watch the AI write architecture instead of spaghetti.


try the Premium Stack

If you want the extended version with Stripe subscription management, Resend email templates, n8n automation workflows, and the complete "Vibe Coding Masterclass" -- it's linked in the repo's README.


What hallucination patterns have you been fighting? I'm building new rules every week -- drop your worst AI-generated bugs in the comments and I'll turn them into constraints.

Follow me for more posts on AI-assisted architecture. Next up: "How I use MCP servers to give Cursor superpowers."

Top comments (0)