Skip to content

The 500 that only showed up on Vercel

KalamAI built clean on my laptop and 500'd in production. The bug was hiding where next build cannot see it -- inside the serverless lambda.

SD
Shubham Datarkar
· 2 min read

KalamAI built clean on my machine. next build passed, next start served the page, every route loaded. Then I pushed it, and the article-analysis step threw a 500 in production before it even reached the auth check. Same code, different result.

That gap -- green locally, red on Vercel -- is the worst kind of bug, because none of my usual tools could see it. The build was happy. The types were happy. The lambda was not.

KalamAI is the writing tool I have been grinding on all week. One of its steps pulls a web page, strips it down to readable text, and analyses it. For that stripping I was using jsdom, which is the obvious, boring choice for parsing HTML in Node -- exactly the kind of pick you do not think twice about.

What actually broke

jsdom does not survive Vercel's serverless runtime. Under the lambda it hits an ERR_REQUIRE_ESM at load time -- the module system it expects is not the one it gets. And because that fires when the route loads, not when you call it, the whole endpoint 500s before a single line of my own logic runs. On my laptop none of this happens, so next start sails through and lies to your face.

I chased it the ugly way. First I tried telling the bundler to leave jsdom external instead of packing it in. No change. So I dropped a temporary diagnostic route into the app just to get the real error out of the lambda -- and immediately tripped over another convention I had forgotten: in this version of Next, folders prefixed with an underscore are treated as private and never become routes. My diag route simply did not exist until I renamed it. Small thing, twenty wasted minutes.

Once the route finally spoke, the answer was obvious. Stop fighting jsdom. I ripped it out and swapped in linkedom, a lighter HTML parser that does not care which runtime it lives in. The analysis step loaded. The 500 was gone.

A passing local build is not evidence. When the failure lives in the lambda, next start will pass and tell you nothing.

note to self

What I took from it

The only honest signal for this class of bug is the deployed function itself. Next time I hit a prod-only 500, the diagnostic route comes first, not after an hour of guessing at bundler config. And maybe I stay a little more suspicious of libraries that assume they are running on a full machine.

With that unblocked I got back to the actual point of the day -- teaching KalamAI to write from crawled facts instead of thin air, and adding a Word export so a draft can leave the tool as something you can actually hand off. More on that once it stops embarrassing me.

Reactions

How was this article?

Newsletter

Never miss the next one

Get the latest playbooks, build logs, and the occasional unpublished idea straight to your inbox. One signal a week — no noise.