trigger-dev — quality + safety report
In the Skillier index (antigravity__trigger-dev) · scanned 2026-06-03 · engine: builtin+triage
1 heuristic flag to review
Heuristic flags from the builtin scanner, which is known to over-flag (it trips on legitimate env-reading integrations, security skills, and library .eval calls). This is NOT an authoritative malicious verdict — re-scan with SkillSpector for the authoritative result. Run the authoritative scan →
📇 This skill is in the Skillier index (curated · deduped · quality-filtered). Install Skillier to route & load it into your AI client.
Quality notes
About this skill
Trigger.dev expert for background jobs, AI workflows, and reliable
📄 Read the SKILL.md
---
name: trigger-dev
description: Trigger.dev expert for background jobs, AI workflows, and reliable
async execution with excellent developer experience and TypeScript-first
design.
risk: unknown
source: vibeship-spawner-skills (Apache 2.0)
date_added: 2026-02-27
---
# Trigger.dev Integration
Trigger.dev expert for background jobs, AI workflows, and reliable async
execution with excellent developer experience and TypeScript-first design.
## Principles
- Tasks are the building blocks - each task is independently retryable
- Runs are durable - state survives crashes and restarts
- Integrations are first-class - use built-in API wrappers for reliability
- Logs are your debugging lifeline - log liberally in tasks
- Concurrency protects your resources - always set limits
- Delays and schedules are built-in - no external cron needed
- AI-ready by design - long-running AI tasks just work
- Local development matches production - use the CLI
## Capabilities
- trigger-dev-tasks
- ai-background-jobs
- integration-tasks
- scheduled-triggers
- webhook-handlers
- long-running-tasks
- task-queues
- batch-processing
## Scope
- redis-queues -> bullmq-specialist
- pure-event-driven -> inngest
- workflow-orchestration -> temporal-craftsman
- infrastructure -> infra-architect
## Tooling
### Core
- trigger-dev-sdk
- trigger-cli
### Frameworks
- nextjs
- remix
- express
- hono
### Integrations
- openai
- anthropic
- resend
- stripe
- slack
- supabase
### Deployment
- trigger-cloud
- self-hosted
- docker
## Patterns
### Basic Task Setup
Setting up Trigger.dev in a Next.js project
**When to use**: Starting with Trigger.dev in any project
// trigger.config.ts
import { defineConfig } from '@trigger.dev/sdk/v3';
export default defineConfig({
project: 'my-project',
runtime: 'node',
logLevel: 'log',
retries: {
enabledInDev: true,
default: {
maxAttempts: 3,
minTimeoutInMs: 1000,
maxTimeoutInMs: 10000,
factor: 2,
},
},
});
// src/trigger/tasks.ts
import { task, logger } from '@trigger.dev/sdk/v3';
export const helloWorld = task({
id: 'hello-world',
run: async (payload: { name: string }) => {
logger.log('Processing hello world', { payload });
// Simulate work
await new Promise(resolve => setTimeout(resolve, 1000));
return { message: `Hello, ${payload.name}!` };
},
});
// Triggering from your app
import { helloWorld } from '@/trigger/tasks';
// Fire and forget
await helloWorld.trigger({ name: 'World' });
// Wait for result
const handle = await helloWorld.trigger({ name: 'World' });
const result = await handle.wait();
### AI Task with OpenAI Integration
Using built-in OpenAI integration with automatic retries
**When to use**: Building AI-powered background tasks
import { task, logger } from '@trigger.dev/sdk/v3';
import { openai } from '@trigger.dev/openai';
// Configure OpenAI with Trigger.dev
const openaiClient = openai.configure({
id: 'openai',
apiKey: process.env.OPENAI_API_KEY,
});
export const generateContent = task({
id: 'generate-content',
retry: {
maxAttempts: 3,
},
run: async (payload: { topic: string; style: string }) => {
logger.log('Generating content', { topic: payload.topic });
// Uses Trigger.dev's OpenAI integration - handles retries automatically
const completion = await openaiClient.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'system',
content: `You are a ${payload.style} writer.`,
},
{
role: 'user',
content: `Write about: ${payload.topic}`,
},
],
});
const content = completion.choices[0].message.content;
logger.log('Generated content', { length: content?.length });
return { content, tokens: completion.usage?.total_tokens };
},
});
### Scheduled Task with Cron
Tasks that run on a schedule
**When to use**: Periodic jobs like reports, cleanup, or syncs
import { schedules, task, logger } from '@trigger.dev/sdk/v3';
export const dailyCleanup = schedules.task({
id: 'daily-cleanup',
cron: '0 2 * * *', // 2 AM daily
run: async () => {
logger.log('Starting daily cleanup');
// Clean up old records
const deleted = await db.logs.deleteMany({
where: {
createdAt: { lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) },
},
});
logger.log('Cleanup complete', { deletedCount: deleted.count });
return { deleted: deleted.count };
},
});
// Weekly report
export const weeklyReport = schedules.task({
id: 'weekly-report',
cron: '0 9 * * 1', // Monday 9 AM
run: async () => {
const stats = await generateWeeklyStats();
await sendReportEmail(stats);
return stats;
},
});
### Batch Processing
Processing large datasets in batches
**When to use**: Need to process many items with rate limiting
import { task, logger, wait } from '@trigger.dev/sdk/v3';
export const processBatch = task({
id: 'process-batch',
queue: {
concurrencyLimit: 5, // Only 5 running at once
},
run: async (payload: { items: string[] }) => {
const results = [];
for (const item of payload.items) {
logger.log('Processing item', { item });
const result = await processItem(item);
results.push(result);
// Respect rate limits
await wait.for({ seconds: 1 });
}
return { processed: results.length, results };
},
});
// Trigger batch processing
export const startBatchJob = task({
id: 'start-batch',
run: async (payload: { datasetId: string }) => {
const items = await fetchDataset(payload.datasetId);
// Split into chunks of 100
const chunks = chunkArray(items, 100);
// Trigger parallel batch tasks
const handles = await Promise.all(
chunks.map(chunk => processBatch.trigger({ items: chunk }))
);
logger.log('Started batch processing', {
totalItems: items.length,
batches: chunks.length,
});
return { batches: handles.length };
},
});
### Webhook Handler
Processing webhooks reliably with deduplication
**When to use**: Handling webhooks from Stripe, GitHub, etc.
import { task, logger, idempotencyKeys } from '@trigger.dev/sdk/v3';
export const handleStripeEvent = task({
id: 'handle-stripe-event',
run: async (payload: {
eventId: string;
type: string;
data: any;
}) => {
// Idempotency based on Stripe event ID
const idempotencyKey = await idempotencyKeys.create(payload.eventId);
if (idempotencyKey.isNew === false) {
logger.log('Duplicate event, skipping', { eventId: payload.eventId });
return { skipped: true };
}
logger.log('Processing Stripe event', {
type: payload.type,
eventId: payload.eventId,
});
switch (payload.type) {
case 'checkout.session.completed':
await handleCheckoutComplete(payload.data);
break;
case 'customer.subscription.updated':
await handleSubscriptionUpdate(payload.data);
break;
}
return { processed: true, type: payload.type };
},
});
## Sharp Edges
### Task timeout kills execution without clear error
Severity: CRITICAL
Situation: Long-running AI task or batch process suddenly stops. No error in logs.
Task shows as failed in dashboard but no stack trace. Data partially processed.
Symptoms:
- Task fails with no error message
- Partial data processing
- Works locally, fails in production
- "Task timed out" in dashboard
Why this breaks:
Trigger.dev has execution timeouts (defaults vary by plan). When exceeded, the
task is killed mid-execution. If you're not logging progress, you won't know
where it stopped. This is especially common with AI tasks that can take minutes.
Recommended fix:
# Configure explicit timeouts:
```typescript
export const processDocument = task({
id: 'process-document',
machine: {
preset: 'large-2x', // More resources = longer allowed time
},
run: async (payload) => {
logger.log('Starting document processing', { docId: payload.id });
// Log progress at each step
logger.log('Step 1: Extracting text');
const text = await extractText(payload.fileUrl);
logger.log('Step 2: Generating embeddings', { textLength: text.length });
const embeddings = await generateEmbeddings(text);
logger.log('Step 3: Storing vectors', { count: embeddings.length });
await storeVectors(embeddings);
logger.log('Completed successfully');
return { processed: true };
},
});
```
# For very long tasks, break into subtasks:
- Use triggerAndWait for sequential steps
- Each subtask has its own timeout
- Progress is visible in dashboard
### Non-serializable payload causes silent task failure
Severity: CRITICAL
Situation: Passing Date objects, class instances, or circular references in payload.
Task queued but never runs. Or runs with undefined/null values.
Symptoms:
- Payload values are undefined in task
- Date objects become strings
- Class methods not available
- "Converting circular structure to JSON"
Why this breaks:
Trigger.dev serializes payloads to JSON. Dates become strings, class instances
lose methods, functions disappear, circular refs throw. Your task sees different
data than you sent.
Recommended fix:
# Always use plain objects:
```typescript
// WRONG - Date becomes string
await myTask.trigger({ createdAt: new Date() });
// RIGHT - ISO string
await myTask.trigger({ createdAt: new Date().toISOString() });
// WRONG - Class instance
await myTask.trigger({ user: new User(data) });
// RIGHT - Plain object
await myTask.trigger({ user: { id: data.id, email: data.email } });
// WRONG - Circular reference
const obj = { parent: null };
obj.parent = obj;
await myTask.trigger(obj); // Throws!
```
# In task, reconstitute as needed:
```typescript
run: async (payload: { createdAt: string }) => {
const date = new Date(payload.createdAt);
// ...
}
```
### Environment variables not synced to Trigger.dev cloud
Severity: CRITICAL
Situation: Task works locally but fails in production. Env var that exists in Vercel
is undefined in Trigger.dev. API calls fail, database connections fail.
Symptoms:
- "Environment variable not found"
- API calls return 401 in production tasks
- Works in dev, fails in production
- Database connection errors in tasks
Why this breaks:
Trigger.dev runs tasks in its own cloud, separate from your Vercel/Railway
deployment. Environment variables must be configured in BOTH places. They
don't automatically sync.
Recommended fix:
# Sync env vars to Trigger.dev:
1. Go to Trigger.dev dashboard
2. Project Settings > Environment Variables
3. Add ALL required env vars
# Or use CLI:
```bash
# Create .env.trigger file
DATABASE_URL=postgres://...
OPENAI_API_KEY=sk-...
STRIPE_SECRET_KEY=sk_live_...
# Push to Trigger.dev
npx trigger.dev@latest env push
```
# Common missing vars:
- DATABASE_URL
- OPENAI_API_KEY / ANTHROPIC_API_KEY
- STRIPE_SECRET_KEY
- Service API keys
- Internal service URLs
# Test in staging:
Trigger.dev has separate envs - configure staging too
### SDK version mismatch between CLI and package
Severity: HIGH
Situation: Updated @trigger.dev/sdk but forgot to update CLI. Or vice versa.
Tasks fail to register. Weird type errors. Dev server crashes.
Symptoms:
- Tasks not appearing in dashboard
- Type errors in trigger.config.ts
- "Failed to register task"
- Dev server crashes on start
Why this breaks:
The Trigger.dev SDK and CLI must be on compatible versions. Breaking changes
between versions cause registration failures. The CLI generates types that
must match the SDK.
Recommended fix:
# Always update together:
```bash
# Update both SDK and CLI
npm install @trigger.dev/sdk@latest
npx trigger.dev@latest dev
# Or pin to same version
npm install @trigger.dev/sdk@3.3.0
npx trigger.dev@3.3.0 dev
```
# Check versions:
```bash
npx trigger.dev@latest --version
npm list @trigger.dev/sdk
```
# In CI/CD:
```yaml
- run: npm install @trigger.dev/s
… (truncated)Want a live grade + an embeddable README badge? Run your skill through the free scanner.
Graded independently by Skillproof — nothing to sell the author. Quality is mechanical + corpus-grounded; safety flags are heuristic (builtin+triage), not a malicious verdict.