Fix: Trigger.dev Not Working — Tasks Not Running, Runs Failing, or Dev Server Not Connecting
Quick Answer
How to fix Trigger.dev issues — task definition and triggering, dev server setup, scheduled tasks with cron, concurrency and queues, retries, idempotency, and deployment to Trigger.dev Cloud.
The Problem
A task is defined but triggering it does nothing:
import { task } from '@trigger.dev/sdk/v3';
export const myTask = task({
id: 'my-task',
run: async (payload: { userId: string }) => {
console.log('Running for', payload.userId);
},
});
// trigger() is called but the task never executesOr the dev server can’t connect:
npx trigger.dev dev
# Error: Could not connect to Trigger.devOr a task runs but fails with a timeout:
Run failed: Task execution timed out after 300 secondsWhy This Happens
Trigger.dev v3 runs your task code in a managed runtime (not inside your Next.js server). This architecture has specific requirements:
- Tasks must be in a
trigger/directory — thetrigger.config.tstells the Trigger.dev CLI where to find tasks. Files outside the configured directory aren’t discovered. Each task file must export a task created with thetask()function. trigger.dev devruns a separate process — the dev server connects to Trigger.dev Cloud (or self-hosted) and executes tasks locally. If the connection fails (wrong API key, network issues), tasks are queued but never picked up.- Tasks run in isolated workers — unlike Inngest which calls your HTTP endpoints, Trigger.dev v3 runs your code in its own runtime. This means tasks can’t access your Next.js server’s in-memory state, but they can access databases, APIs, and file systems.
- Triggering requires the SDK — you call
tasks.trigger()ormyTask.trigger()from your application code. The trigger sends a message to Trigger.dev, which then executes the task. If the API key or project reference is wrong, the trigger silently fails.
Fix 1: Set Up Trigger.dev v3
npm install @trigger.dev/sdk
npx trigger.dev init// trigger.config.ts — project configuration
import { defineConfig } from '@trigger.dev/sdk/v3';
export default defineConfig({
project: 'proj_xxxxxxxxxx', // From Trigger.dev dashboard
runtime: 'node',
logLevel: 'log',
retries: {
enabledInDev: true,
default: {
maxAttempts: 3,
minTimeoutInMs: 1000,
maxTimeoutInMs: 10000,
factor: 2,
},
},
dirs: ['./trigger'], // Directory containing task files
});// trigger/example.ts — define a task
import { task, logger } from '@trigger.dev/sdk/v3';
export const processOrder = task({
id: 'process-order',
// Max execution time
maxDuration: 300, // 5 minutes
run: async (payload: { orderId: string; userId: string }) => {
logger.info('Processing order', { orderId: payload.orderId });
// Step 1: Fetch order
const order = await db.query.orders.findFirst({
where: eq(orders.id, payload.orderId),
});
if (!order) {
throw new Error(`Order ${payload.orderId} not found`);
}
// Step 2: Process payment
logger.info('Charging payment', { amount: order.total });
const payment = await chargePayment(order);
// Step 3: Send confirmation
await sendOrderConfirmation(order, payment);
// Return value is stored with the run
return { orderId: order.id, paymentId: payment.id, status: 'completed' };
},
});# Start the dev server
npx trigger.dev dev
# Or with specific config
TRIGGER_SECRET_KEY=tr_dev_xxx npx trigger.dev devFix 2: Trigger Tasks from Your App
// app/api/orders/route.ts — trigger from an API route
import { tasks } from '@trigger.dev/sdk/v3';
import type { processOrder } from '@/trigger/example';
export async function POST(req: Request) {
const { orderId, userId } = await req.json();
// Trigger the task — returns immediately
const handle = await tasks.trigger<typeof processOrder>('process-order', {
orderId,
userId,
});
return Response.json({
message: 'Order processing started',
runId: handle.id, // Track the run
});
}
// Or import the task directly
import { processOrder } from '@/trigger/example';
const handle = await processOrder.trigger({ orderId: '123', userId: '456' });
// Trigger and wait for result
const result = await processOrder.triggerAndWait({ orderId: '123', userId: '456' });
console.log(result.status); // 'completed'
// Batch trigger — multiple payloads
const handles = await processOrder.batchTrigger([
{ payload: { orderId: '1', userId: '100' } },
{ payload: { orderId: '2', userId: '101' } },
{ payload: { orderId: '3', userId: '102' } },
]);Fix 3: Scheduled Tasks (Cron)
// trigger/scheduled.ts
import { schedules, logger } from '@trigger.dev/sdk/v3';
// Cron-based scheduled task
export const dailyReport = schedules.task({
id: 'daily-report',
cron: '0 9 * * *', // Every day at 9 AM UTC
run: async (payload) => {
logger.info('Generating daily report', {
timestamp: payload.timestamp,
lastTimestamp: payload.lastTimestamp,
});
const stats = await generateDailyStats();
await sendReportEmail(stats);
return { reportGenerated: true, stats };
},
});
// Cleanup old data weekly
export const weeklyCleanup = schedules.task({
id: 'weekly-cleanup',
cron: '0 3 * * 0', // Every Sunday at 3 AM
run: async () => {
const deleted = await db.delete(sessions)
.where(lt(sessions.expiresAt, new Date()))
.returning();
logger.info(`Cleaned up ${deleted.length} expired sessions`);
return { deletedCount: deleted.length };
},
});Fix 4: Concurrency and Queues
// trigger/email.ts — rate-limited task
import { task, queue } from '@trigger.dev/sdk/v3';
// Define a queue with concurrency limits
const emailQueue = queue({
name: 'email-queue',
concurrencyLimit: 5, // Max 5 emails sending at once
});
export const sendEmail = task({
id: 'send-email',
queue: emailQueue,
run: async (payload: { to: string; template: string; data: Record<string, any> }) => {
await emailProvider.send({
to: payload.to,
template: payload.template,
data: payload.data,
});
return { sent: true, to: payload.to };
},
});
// Per-key concurrency — one task per user at a time
export const syncUserData = task({
id: 'sync-user-data',
queue: {
name: 'user-sync',
concurrencyLimit: 1,
},
run: async (payload: { userId: string }) => {
// Only one sync runs per user at a time
await performSync(payload.userId);
},
});Fix 5: Retry and Error Handling
// trigger/resilient.ts
import { task, logger, retry } from '@trigger.dev/sdk/v3';
export const callExternalApi = task({
id: 'call-external-api',
retry: {
maxAttempts: 5,
factor: 2, // Exponential backoff
minTimeoutInMs: 1000, // Start at 1s
maxTimeoutInMs: 30000, // Cap at 30s
randomize: true, // Add jitter
},
run: async (payload: { url: string; data: any }) => {
const response = await fetch(payload.url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload.data),
});
if (response.status === 429) {
// Rate limited — throw to trigger retry
const retryAfter = response.headers.get('Retry-After');
throw new Error(`Rate limited. Retry after ${retryAfter}s`);
}
if (response.status >= 500) {
// Server error — throw to trigger retry
throw new Error(`Server error: ${response.status}`);
}
if (response.status === 400) {
// Client error — abort, don't retry
logger.error('Bad request — not retrying', { status: response.status });
throw new retry.AbortTaskRunError('Bad request — invalid payload');
}
return response.json();
},
});
// Handle cleanup on failure
export const riskyTask = task({
id: 'risky-task',
onFailure: async (payload, error, params) => {
// Called after all retries are exhausted
logger.error('Task failed permanently', {
error: error.message,
attempt: params.attempt,
});
await notifySlack(`Task risky-task failed: ${error.message}`);
},
run: async (payload: { jobId: string }) => {
// Task logic
},
});Fix 6: Deploy to Production
# Deploy tasks to Trigger.dev Cloud
npx trigger.dev deploy
# Deploy to a specific environment
npx trigger.dev deploy --env production
# Check deployment status
npx trigger.dev whoami// Environment variables needed:
// TRIGGER_SECRET_KEY=tr_dev_xxx (dev)
// TRIGGER_SECRET_KEY=tr_prod_xxx (production)GitHub Actions deployment:
# .github/workflows/deploy-trigger.yml
name: Deploy Trigger.dev
on:
push:
branches: [main]
paths:
- 'trigger/**'
- 'trigger.config.ts'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx trigger.dev deploy
env:
TRIGGER_SECRET_KEY: ${{ secrets.TRIGGER_SECRET_KEY }}Still Not Working?
Task triggers but never runs — the dev server must be running (npx trigger.dev dev). Without it, triggers are queued but no worker picks them up. Check the Trigger.dev dashboard to see if the run is in “queued” or “executing” state. Also verify TRIGGER_SECRET_KEY is set correctly.
“Could not connect” during dev — check your API key and network. The dev server connects to Trigger.dev Cloud via WebSocket. Corporate firewalls or VPNs may block this. Also verify the project ID in trigger.config.ts matches your dashboard project.
Task times out at 300 seconds — increase maxDuration in the task definition. For long-running tasks, break them into smaller sub-tasks using tasks.triggerAndWait() from within a parent task. Each sub-task gets its own timeout.
Changes to task code aren’t reflected — restart npx trigger.dev dev after modifying trigger.config.ts. For task file changes, the dev server should hot-reload, but if it doesn’t, restart it. In production, you must run npx trigger.dev deploy to push changes.
For related background job issues, see Fix: Inngest Not Working and Fix: Wrangler Not Working.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Inngest Not Working — Functions Not Triggering, Steps Failing, or Events Not Received
How to fix Inngest issues — function and event setup, step orchestration, retries and error handling, cron scheduling, concurrency control, fan-out patterns, and local development with the Dev Server.
Fix: SST Not Working — Deploy Failing, Bindings Not Linking, or Lambda Functions Timing Out
How to fix SST (Serverless Stack) issues — resource configuration with sst.config.ts, linking resources to functions, local dev with sst dev, database and storage setup, and deployment troubleshooting.
Fix: Fastify Not Working — 404, Plugin Encapsulation, and Schema Validation Errors
How to fix Fastify issues — route 404 from plugin encapsulation, reply already sent, FST_ERR_VALIDATION, request.body undefined, @fastify/cors, hooks not running, and TypeScript type inference.
Fix: Better Auth Not Working — Login Failing, Session Null, or OAuth Callback Error
How to fix Better Auth issues — server and client setup, email/password and OAuth providers, session management, middleware protection, database adapters, and plugin configuration.