Netlify Scheduled Functions Debugging in 2026
Production patterns for debugging Netlify Functions with cron triggers. Real error messages, exact setup, and troubleshooting workflows.
TL;DR
Netlify scheduled functions (v2.0+) require proper environment setup, CloudWatch/function logs monitoring, and timezone awareness. Common failures: timeout errors (default 10s limit), missing environment variables, and cron syntax errors. Use netlify dev locally with the --debug-scheduled flag (verify in official docs for current version) and enable function logs in the Netlify UI.
---
The Challenge: Scheduled Functions Are Silent Failures
Unlike HTTP-triggered functions that return status codes, scheduled functions (cron jobs) execute in darkness. They fail silently, leaving your inbox notifications never sent, your database never cleaned, your reports never generated.
Netlify's scheduled functions use AWS EventBridge under the hood. The problem? Debugging feels like reading tea leaves.
Setting Up Scheduled Functions Correctly
First, verify you're on Netlify Functions runtime Node 18.x or later (check your netlify.toml):
```toml [functions] node_bundler = "esbuild" directory = "netlify/functions"
[[scheduled_functions]] function = "cleanup-db" cron = "0 2 * * *" ```
Your function file structure matters:
``` netlify/ ├── functions/ │ └── cleanup-db.ts └── scheduled_functions.json ```
Critical gotcha: The cron field uses UTC. Period. No exceptions. Document this or your 3 AM job runs at 8 AM.
Production-Ready Function Pattern
```typescript // netlify/functions/cleanup-db.ts import { Handler } from "@netlify/functions"; import { createClient } from "@supabase/supabase-js";
const supabase = createClient( process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY! );
interface ScheduledEvent { timestamp: number; }
const handler: Handler = async (event: ScheduledEvent) => {
const startTime = Date.now();
try {
console.log([${new Date().toISOString()}] Cleanup job started);
// Verify required env vars exist
if (!process.env.SUPABASE_URL || !process.env.SUPABASE_SERVICE_KEY) {
throw new Error("Missing SUPABASE env variables");
}
// Your actual work const { error, data } = await supabase .from("sessions") .delete() .lt("expires_at", new Date().toISOString());
if (error) throw error;
const duration = Date.now() - startTime;
console.log([SUCCESS] Deleted ${data?.length || 0} sessions in ${duration}ms);
return {
statusCode: 200,
body: JSON.stringify({
message: "Cleanup completed",
recordsAffected: data?.length,
durationMs: duration,
}),
};
} catch (err) {
const error = err instanceof Error ? err.message : String(err);
console.error([FAILED] ${error});
// Still return 200 to prevent EventBridge retries (unless you want them)
return {
statusCode: 500,
body: JSON.stringify({ error }),
};
}
};
export { handler }; ```
Real Error Messages You'll See
Error 1: Function Timeout
``` 2026-01-15T14:32:10.234Z ERROR Invoke Error {"errorMessage":"Task timed out after 10.00 seconds","errorType":"TimeoutError","stackTrace":[]} ```Fix: Increase timeout in netlify.toml:
```toml
[[functions]]
name = "cleanup-db"
timeout = 30
```
Verify maximum timeout limit in [official Netlify docs](https://docs.netlify.com/functions/overview/?fn-language=ts).
Error 2: Missing Environment Variables
``` 2026-01-15T14:32:10.456Z ERROR Invoke Error {"errorMessage":"Cannot read property 'split' of undefined","errorType":"TypeError","stackTrace":["at Object.<anonymous> (/var/task/cleanup-db.js:1:2500)"]} ```Fix: Ensure variables exist in Netlify UI → Site settings → Build & deploy → Environment. For scheduled functions specifically, they don't inherit from your build environment automatically—you must set them again.
Error 3: Invalid Cron Expression
``` Validation error in netlify.toml: Invalid cron expression 'every tuesday' ```Fix: Use standard 5-field cron format: minute hour day month dayOfWeek. Test at [crontab.guru](https://crontab.guru).
Local Debugging Workflow
Step 1: Simulate Locally
```bash
Start dev server
netlify dev --functions netlify/functionsIn another terminal, manually trigger your function
curl http://localhost:8888/.netlify/functions/cleanup-db ```This tests HTTP access but not the scheduled trigger. For true scheduled simulation:
Step 2: Use Netlify CLI (v17.0+)
```bash
Verify version
netlify --versionTest scheduled function locally
netlify functions:invoke cleanup-db --local ```If this command isn't available, check [netlify-cli releases](https://github.com/netlify/cli/releases).
Step 3: Monitor Live Execution
1. Deploy to production: netlify deploy --prod
2. Go to Netlify UI → Functions → cleanup-db → Recent invocations
3. Click the most recent execution to see logs and timing
4. Check CloudWatch logs (if enabled) for deeper detail
Debugging Tips
Log timestamps. Always include ISO strings:
```typescript
console.log([${new Date().toISOString()}] Event triggered at timestamp: ${event.timestamp});
```
Use structured logging for production: ```typescript const log = (level: string, message: string, data?: unknown) => { console.log(JSON.stringify({ timestamp: new Date().toISOString(), level, message, data, functionVersion: process.env.FUNCTION_VERSION, })); }; ```
Understand retry behavior. By default, EventBridge retries failed functions. Return statusCode: 200 to prevent retries, or handle idempotency if you want retries.
Timezone trap: Your cron runs in UTC regardless of where you deploy. If you need 2 AM PST, calculate: 2 AM PST = 10 AM UTC = 0 10 * * *.
Related Resources
What am I missing?
Comment below with:
I'll update this guide with community findings.