Vercel: 504 timeout on serverless functions [2026 fix]
Serverless functions exceed 10-second timeout limit. Optimize queries, add streaming, or migrate to long-running endpoints.
TL;DR
Cause: Your serverless function exceeded Vercel's 10-second execution timeout (or 60 seconds on Pro/Enterprise plans).
Fix: Add response streaming, optimize database queries with indexes, or migrate heavy computation to background jobs.
---
Console Error Messages
Here are exact 504 errors you'll see in Vercel logs:
``` Error: FUNCTION_TIMEOUT The function execution time exceeded the maximum allowed duration of 10 seconds. ```
``` HTTP/1.1 504 Gateway Timeout vercel-id: sfo1::h7k8j-1702340400000-abc123def456 ```
``` FetchError: request to https://your-api.vercel.app/api/process failed, reason: socket hang up ```
``` ERR_HTTP_REQUEST_TIMEOUT at Runtime.runHandler Timeout waiting for lambda response after 30000ms ```
``` Function failed due to timeout while waiting for response from database query Query execution time: 15234ms (limit: 10000ms) ```
---
Broken Code vs. Fixed Code
Problem 1: Unoptimized Database Query
BROKEN: ```javascript // api/report.js - Takes 12+ seconds export default async function handler(req, res) { const users = await db.query( 'SELECT * FROM users WHERE created_at > NOW() - INTERVAL 30 DAY' ); const processed = users.map(user => { const transactions = db.query( 'SELECT * FROM transactions WHERE user_id = ?', [user.id] ); return { user, transactions }; }); res.json(processed); } ```
FIXED: ```javascript // api/report.js - Uses indexes + JOIN export default async function handler(req, res) { // Single query with JOIN instead of N+1 const data = await db.query(` SELECT u.*, COUNT(t.id) as transaction_count FROM users u LEFT JOIN transactions t ON u.id = t.user_id WHERE u.created_at > NOW() - INTERVAL 30 DAY GROUP BY u.id `); res.json(data); } ```
Why it works: Eliminates N+1 query problem. One JOIN query executes in ~400ms instead of 50+ sequential queries.
---
Problem 2: Missing Response Streaming
BROKEN: ```javascript // api/export.js - Buffers entire response export default async function handler(req, res) { const allData = await fetchMillion Records(); const csv = convertToCSV(allData); // 8+ seconds res.setHeader('Content-Type', 'text/csv'); res.send(csv); } ```
FIXED: ```javascript // api/export.js - Streams response export default async function handler(req, res) { res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', 'attachment; filename="export.csv"'); const stream = await getDataStream(); stream.pipe(res); }
// Or for Edge Runtime (Vercel's fastest option) export const config = { runtime: 'edge' };
export default async function handler(req) { return new Response(getReadableStream(), { headers: { 'Content-Type': 'text/csv' }, }); } ```
Why it works: Starts sending data immediately instead of buffering. Client receives data while server processes more.
---
Problem 3: Blocking Code in Route Handler
BROKEN: ```javascript // api/webhook.js - Synchronous processing blocks response export default async function handler(req, res) { const payment = req.body; // This takes 8 seconds - response waits await updateInventory(payment.items); await sendEmailReceipts(payment.email); await logAnalytics(payment); res.json({ ok: true }); } ```
FIXED: ```javascript // api/webhook.js - Fire-and-forget with Vercel KV import { kv } from '@vercel/kv';
export default async function handler(req, res) { const payment = req.body; // Return immediately, process asynchronously await kv.lpush('payment-queue', JSON.stringify(payment)); res.json({ ok: true }); }
// cron.js - Separate job processor export const config = { maxDuration: 300, // 5 minutes allowed };
export default async function handler(req, res) { const payment = await kv.lpop('payment-queue'); await updateInventory(payment.items); await sendEmailReceipts(payment.email); await logAnalytics(payment); res.json({ processed: true }); } ```
Why it works: API returns in <100ms. Queue job processes separately with 5-minute timeout.
---
Still Broken? Check These Too
1. Cold start delays: First invocation takes 1-2 seconds. If your function already uses 9 seconds, cold start pushes it over. Solution: Use Vercel's [continuous deployment](/?guide=vercel-cold-starts) with pre-warming or upgrade to Pro plan for faster provisioning.
2. External API timeouts: Your function calls slow third-party APIs (Stripe, Shopify, etc.). Add retry logic with exponential backoff and timeout wrapping: ```javascript const timeout = (promise, ms) => Promise.race([ promise, new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms)) ]); ```
3. Memory constraints: Memory pressure slows execution. Check CloudWatch/Vercel logs for "Memory Limit Exceeded." If using Pro, increase allocated memory via vercel.json:
```json
{ "functions": { "api/**": { "memory": 3008 } } }
```
---
Version Notes
I'm uncertain whether Vercel's Edge Runtime timeout behavior changed between early 2025 and 2026. Edge Functions officially support 30-second timeouts, but regional variations may exist. Check your Vercel dashboard's "Function Metrics" tab to confirm your actual timeout ceiling.
---
Official References:
Related Guides:
---
Found a different variation? Drop it in the commentsβwe're building the 2026 Vercel troubleshooting guide together.