Netlify Scheduled Functions Debugging in 2026
Debug Netlify scheduled functions with exact error patterns, logging strategies, and production-ready code examples for indie hackers.
TL;DR
Netlify scheduled functions (functions v2+) fail silently without proper logging. Use structured JSON logging, CloudWatch integration, and local emulation withnetlify dev. Common errors: timezone mismatches, missing environment variables, and handler timeouts. Always test locally before deploying.Why Scheduled Functions Are Hard to Debug
Scheduled functions run asynchronously outside user requests, making them invisible to traditional debugging. Unlike HTTP functions with immediate response feedback, scheduled functions execute in isolation—if they fail, you'll only know if you're actively monitoring.
Netlify's scheduler uses AWS EventBridge under the hood (verify in [official Netlify functions docs](https://docs.netlify.com/functions/overview/)), which means debugging requires understanding both Netlify's abstraction layer and underlying AWS behavior.
Real Error Messages You'll See
1. Silent Timeout (Most Common)
``` Task timed out after 10.00 seconds ``` This appears in Netlify's function logs but your logs might not capture it. The function hits Netlify's 10-second default timeout for scheduled functions. Verify timeout limits in official docs as these may change per plan tier.2. Missing Environment Variable at Runtime
``` ReferenceError: Cannot read property 'STRIPE_API_KEY' of undefined at Object.<anonymous> (/var/task/api/charge.js:5:15) ``` Environment variables loaded during build aren't available in scheduled function context. They must be explicitly available at function invocation time.3. Timezone-Related Logic Failure
``` Scheduled function executed but condition never met: if (new Date().getHours() === 14) // Always false outside UTC ``` Scheduled functions run in UTC by default. Application logic usingnew Date() without timezone awareness causes drift.Production-Ready Debug Pattern
Here's the minimal logging structure that catches 90% of scheduled function issues:
```javascript // netlify/functions/cleanup-old-records.js import { schedule } from '@netlify/functions';
const handler = async () => {
const startTime = Date.now();
const executionId = exec-${Date.now()}-${Math.random().toString(36).slice(2)};
// Structured logging - parse JSON in Netlify Analytics
const log = (level, message, data = {}) => {
const logEntry = {
timestamp: new Date().toISOString(),
executionId,
level,
message,
...data,
runtime: Date.now() - startTime,
};
console.log(JSON.stringify(logEntry));
};
try { log('INFO', 'Scheduled cleanup starting', { timezone: process.env.TZ || 'UTC', environment: process.env.NODE_ENV, });
// Validate environment at runtime (not build time)
const requiredEnvVars = ['DATABASE_URL', 'CLEANUP_THRESHOLD_DAYS'];
const missing = requiredEnvVars.filter(key => !process.env[key]);
if (missing.length > 0) {
throw new Error(Missing required env vars: ${missing.join(', ')});
}
const thresholdDate = new Date(); thresholdDate.setDate(thresholdDate.getDate() - parseInt(process.env.CLEANUP_THRESHOLD_DAYS)); log('DEBUG', 'Cleanup threshold calculated', { thresholdDate: thresholdDate.toISOString(), thresholdDays: process.env.CLEANUP_THRESHOLD_DAYS, });
// Simulated database operation with timeout protection const dbResponse = await Promise.race([ deleteRecordsBefore(thresholdDate), new Promise((_, reject) => setTimeout(() => reject(new Error('Database operation timeout after 8s')), 8000) ), ]);
log('INFO', 'Cleanup completed successfully', { recordsDeleted: dbResponse.count, affectedTables: dbResponse.tables, });
return { statusCode: 200, body: JSON.stringify({ success: true, executionId }), }; } catch (error) { log('ERROR', error.message, { stack: error.stack, errorType: error.constructor.name, });
// Return 200 to prevent Netlify retry loops // Errors should be tracked separately return { statusCode: 200, body: JSON.stringify({ success: false, error: error.message, executionId, }), }; } };
// Cron syntax: "min hour day month day-of-week" // Run daily at 2 AM UTC export const config = { schedule: '0 2 * * *', };
export default schedule(handler);
// Mock function - replace with actual database call async function deleteRecordsBefore(date) { return { count: 42, tables: ['sessions', 'logs'] }; } ```
Local Testing Setup
Test scheduled functions locally before deploying:
```bash
Install Netlify CLI v12.0.0+ (verify in official docs)
npm install -g netlify-cliStart local server with function emulation
netlify dev ```Then trigger your function manually:
```bash
In another terminal
curl http://localhost:8888/.netlify/functions/cleanup-old-records ```For cron simulation, use [netlify dev --debug](https://docs.netlify.com/cli/get-started/) to see scheduled function execution logs in real-time.
Environment Variable Trap
Scheduled functions load environment from your deployment context, not your .env file:
```javascript // ❌ WRONG: Relies on build-time env const apiKey = process.env.STRIPE_KEY; // undefined at runtime
// ✅ CORRECT: Load at function invocation import fetch from 'node-fetch';
const handler = async () => { const apiKey = process.env.STRIPE_KEY; // Set in Netlify Site Settings if (!apiKey) throw new Error('STRIPE_KEY not configured'); // ... }; ```
Set variables in Netlify: Site Settings → Build & Deploy → Environment
Monitoring in Production
Scheduled functions don't appear in your application logs automatically. Enable monitoring:
1. Netlify Analytics: Check function invocations in Deploys → Function logs 2. External logging: Send structured logs to Datadog, LogRocket, or similar 3. Alerting: Set up notifications for execution failures
```javascript // Send logs to external service const sendToDatadog = async (logEntry) => { if (process.env.NODE_ENV === 'production') { await fetch('https://api.datadoghq.com/api/v2/logs', { method: 'POST', headers: { 'DD-API-KEY': process.env.DATADOG_API_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ message: logEntry }), }); } }; ```
Common Cron Mistakes
```javascript // ❌ Every minute (too aggressive) export const config = { schedule: '* * * * *' };
// ✓ Daily at 9 AM UTC export const config = { schedule: '0 9 * * *' };
// ✓ Every weekday at 3 PM UTC export const config = { schedule: '0 15 * * 1-5' }; ```
Verify syntax at [crontab.guru](https://crontab.guru/).
Quick Debug Checklist
statusCode and bodySee also: [netlify functions best practices](/?guide=netlify-functions) and [serverless logging patterns](/?guide=serverless-logging)
What am I missing?
Have you debugged scheduled functions with edge cases? Drop your findings in the comments—timezone bugs, memory constraints, or specific error messages that stumped you. Also, please verify the timeout limits and pricing tier details mentioned here against current Netlify documentation if you're reading this significantly after 2026.