Symptoms
- The AI feature (chat, summarize, generate) works on localhost and in the v0 or Cursor preview, but the live site shows an error or spins forever.
- Vercel returns 504 with
FUNCTION_INVOCATION_TIMEOUT(Vercel: FUNCTION_INVOCATION_TIMEOUT). - Short prompts work, long ones fail.
- Function logs show the request started but never finished.
Why it happens
Vercel's error page lists three causes (Vercel):
- The function takes too long. API or database calls run past the maximum duration for your plan.
- The function never returns a response. "The function must return an HTTP response, even if that response is an error. If no response is returned, the function will time out."
- Upstream errors. The external API or database you call is failing or hanging.
AI calls hit all three: model responses can take a long time, and generated code often has a try without a return in the catch.
Current limits (Vercel Functions with Fluid compute, from Vercel: Functions limits, fetched 19 Oct 2026):
| Plan | Default | Maximum |
|---|---|---|
| Hobby | 300 s | 300 s |
| Pro | 300 s | 800 s (1800 s extended, beta) |
| Enterprise | 300 s | 800 s (1800 s extended, beta) |
Vercel says that "As of April 23, 2025, fluid compute is enabled by default for new projects" (Vercel: Fluid compute). Older projects may not have it on. The limits above are for Fluid compute, so check it's on for your project first (step 1).
Note: the maximum duration "includes time spent processing the request and sending the response, including streamed responses" (Vercel limits). Streaming gets the first words to the user sooner, but it doesn't give a function unlimited time.
Step-by-step fix
1. Check Fluid compute
Vercel's KB gives the path: open your project, Settings → Functions, find Fluid Compute, enable the toggle, save, and redeploy (Vercel KB: functions timing out).
2. Set maxDuration on the AI route
In the Next.js App Router, export it from the route file (Vercel: Configuring maximum duration):
// app/api/chat/route.ts
export const maxDuration = 120 // seconds; must be within your plan's maximum3. Stream the model's response
Vercel recommends streaming for AI applications, "so the response starts returning before the full computation finishes" (Vercel KB). With the OpenAI Responses API, streaming events of type response.output_text.delta carry the text (OpenAI: Streaming API responses):
// app/api/chat/route.ts
import OpenAI from 'openai'
export const maxDuration = 120
const openai = new OpenAI() // reads OPENAI_API_KEY on the server
export async function POST(req: Request) {
try {
const { prompt } = await req.json()
const stream = await openai.responses.create(
{ model: process.env.OPENAI_MODEL!, input: prompt, stream: true },
{ signal: AbortSignal.timeout(100_000) } // give up before Vercel does
)
const encoder = new TextEncoder()
const body = new ReadableStream({
async start(controller) {
try {
for await (const event of stream) {
if (event.type === 'response.output_text.delta') {
controller.enqueue(encoder.encode(event.delta))
}
}
} catch {
controller.enqueue(encoder.encode('\n[The answer was cut off. Please try again.]'))
} finally {
controller.close()
}
},
})
return new Response(body, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } })
} catch (err) {
// Always return something, even on error
return Response.json({ error: 'The AI request failed. Please try again.' }, { status: 502 })
}
}The { signal } request option and AbortSignal.timeout are standard; check your installed openai package version supports per-request options. Try it on a preview deployment before you ship it.
4. Return a response on every path
Search the route for any catch or early exit that doesn't return. That alone can cause the timeout.
5. Move long jobs out of the request
For work that runs longer than a request should (batch generation, long documents), Vercel points to waitUntil (or after in Next.js) for background tasks after the response, and to Vercel Workflow for durable, longer-running jobs (Vercel KB). Return a job id right away and let the page poll for the result.
6. Keep the key on the server
Don't "fix" timeouts by calling OpenAI directly from the browser. That exposes your key. See OpenAI API key exposed in your Lovable or Bolt app?
Fix prompt for your AI builder
Paste this into Lovable, Bolt, Cursor, Replit, v0 or whichever tool you build with:
My AI route on Vercel fails with 504 FUNCTION_INVOCATION_TIMEOUT. Fix only that route:
1. Export maxDuration from the route file (start with 120 seconds; tell me if my plan allows it).
2. Stream the model output to the client instead of waiting for the full response (OpenAI Responses API with stream: true, sending response.output_text.delta chunks), and update the frontend to render the stream as it arrives.
3. Add a timeout to the upstream AI call that's shorter than maxDuration, and make every code path (including catch blocks) return a Response with a clear error message.
4. Keep the OpenAI key server-side only. Never call OpenAI from browser code.
Don't change the prompt, model choice or UI design beyond rendering the stream.How to confirm it's fixed
- Redeploy, then run your longest realistic prompt on the live site.
- Text should start appearing within a few seconds and finish without a 504.
- In Vercel's function logs, check the duration of the request and that no
FUNCTION_INVOCATION_TIMEOUTappears. - Simulate an upstream failure (for example a wrong model name in Preview) and check the page shows your error message instead of hanging.
How to find out sooner next time
Heedza doesn't use your AI features: it never submits forms or types prompts. It checks on a schedule that your landing, signup, login and checkout pages load and show their forms, and sends a screenshot and a fix prompt when one doesn't. A page that times out while rendering would show up there. The free scan checks your homepage and a few of its scripts for exposed keys. It flags OpenAI project, service-account and admin keys as critical.
Let Heedza check this for you. Heedza’s online and page check keeps checking that your live home page opens with real content, and that your sign-up, log-in and payment pages still open and show their forms and Pay buttons. It never logs in or fills in forms, so it can’t see errors that only show after that.
Made with Lovable, Bolt, Replit, Cursor or v0? Free for 1 site: start free or see prices.