From cba67a55ede722d4e06364b7f7f6dcfffd85a858 Mon Sep 17 00:00:00 2001 From: vimzh Date: Sun, 24 May 2026 11:45:05 +0530 Subject: [PATCH 1/2] fix(onboarding): fail fast with a clear error when EXA_API_KEY is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The route previously fell back to "x-api-key": process.env.EXA_API_KEY ?? "", so a missing env var produced an outbound call to Exa with an empty key. Exa returned an auth failure, which was then surfaced to the client as a generic 500 "Failed to fetch content from Exa API" — making missing configuration indistinguishable from upstream errors and slowing self-hosted setup. Read EXA_API_KEY once at the top of the handler, log a specific message when it's absent, and return a 500 with a clear "service is not configured" error. The downstream fetch now uses the validated, non-empty key directly. --- apps/web/app/api/onboarding/extract-content/route.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/web/app/api/onboarding/extract-content/route.ts b/apps/web/app/api/onboarding/extract-content/route.ts index 6cdb40d5c..6378057d1 100644 --- a/apps/web/app/api/onboarding/extract-content/route.ts +++ b/apps/web/app/api/onboarding/extract-content/route.ts @@ -11,6 +11,15 @@ interface ExaApiResponse { export async function POST(request: Request) { try { + const exaApiKey = process.env.EXA_API_KEY + if (!exaApiKey) { + console.error("EXA_API_KEY is not configured") + return Response.json( + { error: "Content extraction service is not configured" }, + { status: 500 }, + ) + } + const { urls } = await request.json() if (!Array.isArray(urls) || urls.length === 0) { @@ -30,7 +39,7 @@ export async function POST(request: Request) { const response = await fetch("https://api.exa.ai/contents", { method: "POST", headers: { - "x-api-key": process.env.EXA_API_KEY ?? "", + "x-api-key": exaApiKey, "Content-Type": "application/json", }, body: JSON.stringify({ From 494dd25cf8f9fba0ff10acaf4bef8f638ab0aa5c Mon Sep 17 00:00:00 2001 From: vimzh Date: Sun, 24 May 2026 12:43:12 +0530 Subject: [PATCH 2/2] fix(onboarding): hoist EXA_API_KEY check and return generic 503 Copilot review flagged two things: 1. console.error inside the handler fires on every request when the key is missing, which spams logs and can mask other errors. Moved the read to module scope so the warning fires once at module load. 2. 'Content extraction service is not configured' tells the client a bit too much about deployment state. Reply with a generic 'Content extraction is unavailable' instead, while keeping the detailed reason in the server log. Also bumped the status from 500 to 503 since 'service unavailable due to missing config' is what we are actually telling the client now. --- .../web/app/api/onboarding/extract-content/route.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/web/app/api/onboarding/extract-content/route.ts b/apps/web/app/api/onboarding/extract-content/route.ts index 6378057d1..9322f3242 100644 --- a/apps/web/app/api/onboarding/extract-content/route.ts +++ b/apps/web/app/api/onboarding/extract-content/route.ts @@ -9,14 +9,19 @@ interface ExaApiResponse { results: ExaContentResult[] } +const exaApiKey = process.env.EXA_API_KEY +if (!exaApiKey) { + console.error( + "EXA_API_KEY is not configured; /api/onboarding/extract-content will return 503", + ) +} + export async function POST(request: Request) { try { - const exaApiKey = process.env.EXA_API_KEY if (!exaApiKey) { - console.error("EXA_API_KEY is not configured") return Response.json( - { error: "Content extraction service is not configured" }, - { status: 500 }, + { error: "Content extraction is unavailable" }, + { status: 503 }, ) }