← FilingCheck

Why your second Vercel Python route silently 404s

FilingCheck runs three surfaces off one SEC-filing analysis engine: a Poe bot, a ChatGPT Apps (MCP) endpoint, and a self-serve /buy page with its own Stripe checkout. My first instinct was to keep them apart -- api/index.py for the Poe bot, a new api/mcp.py for the MCP routes, each with its own FastAPI app object. Clean separation, easy to reason about.

It deployed without error. /mcp returned a 404. So did every other path I tried on that file. No traceback, no build warning -- just a 404 that looked exactly like a route that had never been registered.

The actual behavior: with no vercel.json in the project, Vercel's zero-config Python builder treats the entire api/ directory as the source for one lambda, and that lambda serves whichever file's app object the builder happens to pick -- in this case index.py. mcp.py was bundled into the deployment (imports resolved, no error) but never actually invoked for any request. Every path, including ones mcp.py was supposed to own, got routed to index.py's app and fell through to its default 404 handler. There's no log line that says "wrong file" -- you just get a plausible-looking, completely wrong 404.

A second, smaller version of the same class of bug: that single lambda also doesn't add its own directory to sys.path by default, so a plain from bot_core import X import between sibling files in api/ can 500 with no traceback visible anywhere -- Vercel's deployment-events API only exposes build logs, not runtime ones. The fix there is sys.path.insert(0, os.path.dirname(__file__)) before the import, and if you need to see a real traceback, temporarily wrap the risky import in try/except and expose it via a throwaway route.

The real fix for the routing issue: stop pretending there are multiple files with independent authority. Mount every route -- Poe's, MCP's, the Stripe checkout flow's, robots.txt, sitemap.xml -- onto the single app object the one lambda actually serves. That's what this file does now: one app, every path added onto it explicitly with app.add_api_route, no sibling file pretending to be its own service.

If you're debugging a Vercel Python route that 404s despite deploying clean, check for exactly this shape first: is this route living in a different file from the one Vercel is actually routing to? If your project has no vercel.json, the answer is almost certainly yes.

This is the same engine behind FilingCheck -- $2 to check a stock's Graham fair value and margin of safety against its actual SEC 10-K numbers, no LLM guessing the figures.