Cron Jobs Not Running

I’m on the Hobby plan. I have 2 cron jobs set up: /api/send-email (0 2 * * 5) and /api/send-churn-followup-email (5 17 * * 0-4,6). The cron schedules don’t overlap (second doesn’t run on day 5), so I shouldn’t be exceeding my Hobby limit of 1 per day. However, I’ve found that the functions don’t run at all. I’ve waited through the 1 hour window and a 24 hour window. I don’t see anything in the logs. I know my functions work because pressing the “Run” button returns status code 200 and I can see the result of the function call in my DB. I have also been through the Cron Job troubleshooting guide with no luck.

Current behavior: function doesn’t run without clicking run manually
Expected behavior: function runs at schedule time

Here’s how both routes are set up:

import { createClient, SupabaseClient } from '@supabase/supabase-js';
import { Resend } from 'resend';
import { NextResponse } from 'next/server';

export const dynamic = 'force-dynamic';

const resend = new Resend(process.env.RESEND_API_KEY);

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
const supabase = createClient(supabaseUrl, supabaseServiceRoleKey);

async function* getAllUsers(supabase: SupabaseClient) {
   ...
}

export async function GET(request: Request) {
  try {
    const authHeader = request.headers.get('Authorization');
    const token = authHeader?.split(' ')[1];  // Extract token from "Bearer <token>"
    
    if (!token || token !== process.env.CRON_SECRET) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    } 
  
  // Actual function - creating and sending an email with Resend based on Supabase data

  // Wait for all emails to be sent
    await Promise.all(batchPromises);

  return NextResponse.json({ 
      message: 'Follow-up emails sent successfully',
      emailsSent: targetUsers.length
    }, { status: 200 });

  } catch (error) {
    console.error('Error sending follow-up emails:', error);
    return NextResponse.json({ error: 'Failed to send follow-up emails' }, { status: 500 });
  }
}

Project information: Hobby plan, production environment, Next.js, Login – Vercel

Update on this - my 5 17 * * 0-4,6 job seems to have run by itself yesterday, but nothing ran today (I made new production deployments as usual, but didn’t make any changes to anything that would affect these routes).

Hi @rohantaneja, welcome to the Vercel Community!

The cron expressions seem correct.

Two points I’d like to share:

  • The cron job timings are UTC so 17:05 would be UTC and not your local timezone.
  • Your project is on the hobby plan so logs beyond 1hr are not retained. Maybe that’s why you don’t see any logs when you come back later to check.

I hope this was helpful.

Hi Anshuman, thank you for your response! 17:05 UTC is 9:05am PST. I’ve checked the cron job logs continuously from 8:00am to 10:15am and they were empty. The cron jobs also make changes to my database. I see these changes in Supabase when I click “Run” but not otherwise, meaning the jobs aren’t running.

Hi @rohantaneja, thanks for providing more information.

Can you share how are you setting up the cron jobs?

I created a vercel.json file:

{
  "crons": [
    {
      "path": "/api/send-email",
      "schedule": "0 2 * * 5"
    },
    {
      "path": "/api/send-churn-followup-email",
      "schedule": "5 17 * * 0-4,6"
    }
  ]
}

What’s interesting is that the /api/send-churn-followup-email route ran this morning automatically, but yesterday I had to run it manually.

1 Like

Hi @rohantaneja, thanks for sharing your cron configuration.

On the hobby plan the cron jobs might run in the 1hr window of the scheduled time based on how busy the schedule is. So, it can be that we miss the logs because we can’t catch the function running.

Now, because you mentioned that the database get updated when you run the job manually it works.

Can you confirm these are the only two cron jobs you’ve on your account? Because the 2 cron job limit is across the account and not per project.

Yes, these are the only two cron jobs on my account

Hi @rohantaneja, thanks for confirming.

Can you try a different schedule for testing? Maybe keep 1 day gap in between just so we are sure it’s not about the scedule.

If you are using Next.js 14/15 with Route handler. Can you try adding to the top of the files to avoid caching?

export const dynamic = 'force-dynamic';