Answer

This is by design — `redirect()` in Next.js works by throwing a special `NEXT_REDIRECT` error internally. When you wrap it in try/catch, your catch block intercepts it. **Clean pattern — move redirect outside the try/catch:** ```typescript async function createPost(formData: FormData) { 'use server' let success = false try { await db.insert(posts).values({ title: formData.get('title') }) success = true } catch (err) { return { error: 'Failed to create post' } } if (success) redirect('/posts') } ``` **Alternative — re-throw redirect errors:** ```typescript import { isRedirectError } from 'next/dist/client/components/redirect' async function createPost(formData: FormData) { 'use server' try { await db.insert(posts).values({ title: formData.get('title') }) redirect('/posts') } catch (err) { if (isRedirectError(err)) throw err // let redirect propagate return { error: 'Failed to create post' } } } ``` The first pattern is cleaner — keep the redirect as the last statement after all fallible work is done, outside any try/catch scope.

b24872b0-adc8-44b7-ac30-8f9fab3a8a16

This is by design — redirect() in Next.js works by throwing a special NEXT_REDIRECT error internally. When you wrap it in try/catch, your catch block intercepts it.

Clean pattern — move redirect outside the try/catch:

async function createPost(formData: FormData) {
  'use server'
  let success = false
  try {
    await db.insert(posts).values({ title: formData.get('title') })
    success = true
  } catch (err) {
    return { error: 'Failed to create post' }
  }
  if (success) redirect('/posts')
}

Alternative — re-throw redirect errors:

import { isRedirectError } from 'next/dist/client/components/redirect'

async function createPost(formData: FormData) {
  'use server'
  try {
    await db.insert(posts).values({ title: formData.get('title') })
    redirect('/posts')
  } catch (err) {
    if (isRedirectError(err)) throw err  // let redirect propagate
    return { error: 'Failed to create post' }
  }
}

The first pattern is cleaner — keep the redirect as the last statement after all fallible work is done, outside any try/catch scope.