Netlify Scheduled Functions Debugging in 2026
Debug Netlify scheduled functions like a pro. Real error patterns, production patterns, and step-by-step troubleshooting for indie hackers.
TL;DR
Netlify scheduled functions (powered by AWS EventBridge) fail silently by default. Enable CloudWatch logging, validate cron expressions with 0 */6 * * * format, and use environment variables for debugging. Common issues: timezone mismatches, missing schedule property, and unhandled promise rejections.
---
The Silent Killer: Why Scheduled Functions Fail Without You Knowing
Scheduled functions in Netlify Functions (available since Netlify Functions v2.0) execute on a cron schedule, but debugging is non-obvious. Unlike HTTP functions where errors appear in response bodies, scheduled functions run asynchronously. Your function executes, errors occur, and nobody notices for hours.
The root cause: scheduled functions use AWS EventBridge under the hood, which doesn't automatically surface errors to your dashboard.
---
Setting Up for Debugging Success
Step 1: Enable Netlify Function Logs
First, verify in official docs for current pricing on function invocation costs in your plan. As of early 2026, all plans support scheduled functions, but execution costs vary.
In your netlify.toml:
```toml [functions] node_bundler = "esbuild" external_node_modules = ["axios"]
[[scheduled_functions]] function = "my-scheduled-task" schedule = "0 */6 * * *" timezone = "America/New_York" ```
The schedule field uses standard cron format. Common gotcha: Netlify functions UI doesn't validate syntax—invalid cron expressions silently fail.
Step 2: Create Your Function with Proper Logging
Production-ready scheduled function at netlify/functions/my-scheduled-task.ts:
```typescript import { Handler, ScheduledEvent } from '@netlify/functions'; import * as https from 'https';
const handler: Handler = async (event: ScheduledEvent) => {
console.log([${new Date().toISOString()}] Scheduled function triggered);
console.log('Event source:', event.source); // Should be 'aws.events'
console.log('Event detail-type:', event['detail-type']);
try { // Your actual logic here const result = await processData(); console.log('Processing succeeded:', result); return { statusCode: 200 }; } catch (error) { // CRITICAL: Always log errors explicitly console.error('Processing failed:', { message: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString(), }); // Don't throw—scheduled functions treat errors as fatal retries return { statusCode: 500 }; } };
async function processData(): Promise<string> { // Simulate work return 'success'; }
export { handler }; ```
---
Real Error Messages You'll See (And How to Fix Them)
Error 1: Invalid cron expression
``` [ERROR] ValidationException: 1 validation error detected: Value '* */6 * * *' at 'ScheduleExpression' failed to satisfy constraint: Schedule expressions must be in cron format or rate format ```
Fix: Cron format is minute hour day month day-of-week. Test with:
```javascript // Quick validation before deployment const isValidCron = (expr) => { const parts = expr.split(' '); return parts.length === 5 && parts.every((p) => /^(\d+|\*|\/|-)/.test(p)); }; ```
Error 2: Handler crashed during execution
``` 2026-01-15T14:23:45.123Z ERROR Invoke Error { "errorMessage": "Cannot read property 'forEach' of undefined", "errorType": "TypeError", "stackTrace": ["at processData (/var/task/my-scheduled-task.js:12:5)"] } ```
Fix: Unhandled promise rejections. Wrap everything in try-catch and always return a statusCode:
```typescript if (!data) { console.error('Data is undefined, aborting'); return { statusCode: 400 }; // Don't throw } ```
Error 3: Timeout after 900 seconds
``` Task timed out after 900.00 seconds A function can run for a maximum of 15 minutes (900 seconds) before Lambda terminates it ```
Fix: Netlify Functions have a hard 15-minute timeout. For longer tasks, use [Netlify Background Functions](/?guide=background-jobs) instead.
---
Debugging Strategies That Actually Work
Strategy 1: Use Environment Variables for Dry Runs
```typescript const isDryRun = process.env.DRY_RUN === 'true';
const handler: Handler = async (event: ScheduledEvent) => { if (isDryRun) { console.log('[DRY RUN] Would execute at', new Date().toISOString()); return { statusCode: 200 }; } // ... actual execution }; ```
Deploy with netlify env:set DRY_RUN true, test, then remove it.
Strategy 2: Log to External Service
Don't rely solely on Netlify logs. Send critical errors to Sentry or similar:
```typescript import * as Sentry from '@sentry/node';
Sentry.init({ dsn: process.env.SENTRY_DSN });
const handler: Handler = async (event: ScheduledEvent) => { try { await riskyOperation(); } catch (error) { Sentry.captureException(error, { tags: { function: 'my-scheduled-task' }, extra: { event }, }); } }; ```
Strategy 3: Validate Timezone Handling
Netlify uses UTC internally. Set timezone in netlify.toml to your preference:
```toml [[scheduled_functions]] function = "report-daily" schedule = "0 9 * * *" timezone = "Europe/London" ```
But always log in UTC for debugging:
```typescript console.log('UTC time:', new Date().toISOString()); console.log('Local Intl:', new Intl.DateTimeFormat('en-US', { timeZone: 'Europe/London', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', }).format(new Date())); ```
---
Checking Execution History
Netlify doesn't provide a built-in execution history UI. Check via AWS CLI if you have credentials:
```bash aws events list-targets-by-rule \ --rule netlify-scheduled-function-my-scheduled-task ```
Or use the [Netlify API](/?guide=netlify-api) to query function logs programmatically.
---
Production Checklist
netlify.toml---
Resources
---
What am I missing?
Have you debugged scheduled functions on Netlify? Did you hit errors not covered here? Share your war stories, gotchas, and solutions in the comments. Also mention: what's your preferred external logging service? Are there newer patterns in v2.1+ we should highlight?