Vercel Cron Jobs Not Running? 6 Silent Fails (2026)

Yatish Goel
Co-Founder & CTO
Vercel Cron Jobs Not Running? 6 Silent Fails (2026)
If you are a founder or dev on a US-based team, Vercel Cron Jobs feel like free money. You set a schedule, deploy, and forget it.
Then one day your daily sync stops. No errors. No logs. Your customers just start emailing you.
This post is about the failures that waste the most time because they look like “Vercel is broken” when it is usually your app doing something subtle. I have seen this in Next.js apps, Astro apps, and plain Node route handlers.
What makes Vercel cron failures nasty
Most schedulers either succeed or they scream. Vercel Cron is quieter. If your endpoint returns a 3xx, Vercel treats that redirect as the final response for that run. It will not follow it.
Vercel’s own Knowledge Base calls this out: cron jobs do not follow redirects, and if you have Next.js trailingSlash enabled you need to match the slash in the cron path or you can get a silent fail. That single sentence explains a ridiculous number of “cron stopped working” threads.
Another pain: caching. A cron route that gets cached can look like it never ran because it never executed code. You might see no logs, and your downstream work never happens.
Quick diagnosis checklist (do this first)
Before you refactor anything, run through this list. It is the 10 minute version that saves you 2 hours.
1) Confirm the cron is attached to a Production deployment, not Preview.
2) Open Vercel Project Settings -> Cron Jobs -> pick the job -> click “Run”. If manual run produces logs, your code path is fine.
3) Hit the exact cron URL from a terminal and capture status code and any redirects:
curl -i https://your-domain.com/api/cron/whatever
If you see 301 or 308, stop. Fix redirects first.
4) Check response time. If your function is timing out, split the work into pages or queues.
Silent fail #1 (the big one): redirects + trailingSlash
If your project has next.config.js trailingSlash: true, Next.js will redirect /api/cron/daily to /api/cron/daily/. Browsers follow this so you do not notice.
Vercel Cron does not follow redirects. That means your cron request gets a 308, Vercel considers the invocation done, and your code never runs.
This is not theory. A Vercel community thread about cron jobs “stopping” ends with exactly this fix: set trailingSlash to false, or add the slash at the end of the cron job path.
Fix options (pick one):
Option A: Change the cron path to include the trailing slash. In Vercel Cron config, set path to /api/cron/daily/ (note the slash).
Option B: Disable trailingSlash if you do not truly need it. Most apps do not need it for API routes.
Option C: Make the cron endpoint return 200 without redirect. For some setups, you can explicitly handle both routes and avoid automatic redirects.
My opinion: Option A is the least risky. Disabling trailingSlash can have SEO side effects for pages, and you might not want to touch routing rules during an incident.
Silent fail #2: cron runs only on Production
This sounds basic, but it bites teams in the UK and Europe a lot because they test on Preview links and assume schedule will run there.
Vercel Cron Jobs only execute against Production deployments. If you are pointing at a preview URL, it will never trigger.
Fix: promote a production deployment or wire a separate scheduler for staging (GitHub Actions, Cloud Scheduler, or a cheap VPS cron).
Silent fail #3: your endpoint is cached
Next.js in 2026 is fast, but it is also eager to cache. If your cron route handler ends up cached, the request can get served from cache and your code never runs.
Vercel’s troubleshooting guide suggests setting dynamic to force execution:
export const dynamic = 'force-dynamic';
Put that at the top of your route handler (app/api/.../route.ts).
Also watch for these cache traps:
- Returning a Response that can be cached unintentionally.
- Using fetch without cache: "no-store" in a route that you expect to execute every time.
- Doing work in a module scope instead of inside the request handler. That work can run once per warm instance and fool you.
If you want your cron to run business logic, write it like a boring old server endpoint. No magic.
Silent fail #4: CRON_SECRET has a newline
Yes, really. I have seen teams paste secrets from 1Password or a CI output that includes a newline at the end.
Vercel calls out that CRON_SECRET should not contain invalid characters, newlines, or weird symbols that break the auth header.
Fix: re-enter the secret manually, or base64 it if you have to pass something structured. Then update the code that validates it.
Quick test: log the length of the header value (not the secret itself) and compare with what you expect.
Silent fail #5: WAF rules block your cron
If you enabled Vercel WAF rules, bot protection, geo blocks, or strict rate limits, your own cron can get caught in the net.
Cron requests come from Vercel’s infra, not from your office IP. So your “allowlist our IPs” rule might block it.
Fix: add a WAF exception for the cron route, or for requests that include your cron authorization header.
If you are a founder in the US doing B2B SaaS, treat cron endpoints like admin endpoints. Lock them down, but do not block yourself.
Silent fail #6: the cron never registered at build time
This one shows up after repo reorganizations or framework migrations.
Vercel cron registration happens at build time. If your build output does not include the crons property in .vercel/output/config.json, the scheduler has nothing to run.
Vercel’s KB suggests running vercel build --prod locally and checking that config file.
Fix: make sure your vercel.json is in the right place, syntax is correct, and your build step is actually producing the expected output.
Also: if you renamed the route, update the cron path. Cron will happily call an old path and get a 404 every time.
A battle-tested setup for reliable cron work (2026)
If the cron job does anything that matters, do not do all the work inside the cron handler.
Do this instead:
1) Cron endpoint validates auth, then enqueues work (DB row, queue message, or workflow engine).
2) A worker processes jobs in small batches (so timeouts do not kill you).
3) You log a heartbeat row per run: startedAt, finishedAt, itemsProcessed, errorCount.
4) You alert if the heartbeat is missing.
This makes failures visible. It also makes cost predictable.
Cost reality check (rough numbers):
- Engineering time to harden a flaky cron setup: 2 to 6 hours.
- If you add queue + dashboarding: 1 to 2 days.
- The cost of not doing it: one missed billing run or stale data incident easily burns a whole week of trust.
Concrete example: fixing a “runs fine manually” cron
A client app (EU ecommerce) had a daily inventory sync. Manual “Run” worked, schedule did not. There were no logs at 02:00 UTC.
What we found in 20 minutes:
- The cron path was /api/cron/inventory (no slash).
- next.config.js had trailingSlash: true.
- Production requests got a 308 redirect to /api/cron/inventory/. Cron did not follow it.
Fix:
- Updated the cron path to include trailing slash.
- Added export const dynamic = "force-dynamic" just to remove any caching doubt.
- Added a heartbeat log row and a Slack alert if no heartbeat by 03:00 UTC.
Total change: 30 minutes. The “Vercel is flaky” narrative disappeared instantly.
Debug tips I actually use
1) Always check status codes. If it is not 200, treat it as broken.
2) Add one log line at the top of the handler and one at the end. If you only log inside the work loop, you miss early failures.
3) Return plain text like “ok” with a run id. JSON is fine, but keep it simple.
4) If the job can be retried, make it idempotent. Store a run key (date + job name). If the same run key arrives twice, do not double-charge or double-send emails.
5) If you call Stripe, email providers, or anything money-adjacent, treat cron like a payment flow. Assume duplicates happen.
FAQ: “Should I ditch Vercel Cron?”
No. It is fine. But it is not magic.
If you need heavy background processing, use a workflow system (Inngest, Temporal, Cloud Tasks, SQS) and let cron only kick it off.
If you just need a simple ping or cleanup, Vercel Cron is great. Just avoid redirects and caching.
One last opinion: if your cron work is business critical, budget a half day to build observability around it. In 2026, “we will notice if it breaks” is not a plan.
Good luck. And yes, the trailing slash thing is still absurd 🙂
One more gotcha: response codes like 204 can be misleading. Some logging UIs key off bodies. I stick to 200 with a short text body.
Also, if you are in Australia or Canada and schedule jobs around midnight local time, double check time zones. Vercel cron expressions are interpreted in UTC unless you explicitly configure otherwise. That can make a “daily” job run in the middle of your business day.
If you are migrating off a monolith, expect the first week to be noisy. Cron and background tasks are where hidden coupling lives. Plan for it.
If you want the safest pattern: cron endpoint just writes a row to Postgres, then a separate worker picks it up. That way even if the cron hits a temporary error, you have an audit trail that the trigger happened.
---
Related reading

Yatish Goel
Co-Founder & CTO
Full-stack architect with US startup experience and an IIT Kanpur degree. Yatish drives the technical vision at HeyDev, designing robust architectures and leading development across web, mobile, and AI projects.
