#need help with scheduled tasks pls

1 messages · Page 1 of 1 (latest)

twin egret
#

hi everyone,
I'm trying to create a task in my application.
basically, I have an invite being created and when that happens, an event called OnInviteCreated is triggered
in this event, I send an email to the user with the link to accept the invite and I'd also like to call trigger.dev to start a task that, when the invite's expiresAt time is reached, should check if the status is still PENDING
if it is, trigger.dev should mark it as EXPIRED; otherwise, it should ignore it
I'm not sure how to implement this, since the docs mention that some props are required, but my project uses different ones
what would be the best approach?

This is my trigger task:

import { schedules } from "@trigger.dev/sdk/v3";
import { UniqueEntityID } from "@vtal/common/domain/unique-entity-id.vo";
import { InviteTaskService } from "@/modules/invites/application/services/invite-task.service";

export function checkInvitationExpiry(taskService: InviteTaskService) {
    return schedules.task({
        id: "check-invitation-expiry",
        run: async (payload) => {
            await taskService.expireInvite(new UniqueEntityID(payload.externalId));
        },
    });
}

This is my taskService:

import { Injectable } from "@nestjs/common";
import { UniqueEntityID } from "@vtal/common/domain/unique-entity-id.vo";

import { InvitesRepository } from "../repositories/invite.repository";

@Injectable()
export class InviteTaskService {
    constructor(private readonly invitesRepository: InvitesRepository) {}

    async expireInvite(inviteId: UniqueEntityID): Promise<void> {
        // TODO: implement logger
        // biome-ignore lint/suspicious/noConsoleLog: <explanation>
        console.log("EXECUTING TASK WITH ID", inviteId);
        const invite = await this.invitesRepository.findById(inviteId);
        if (!invite || invite.isExpired()) return;

        invite.expire();

        await this.invitesRepository.update(inviteId, invite);
    }
}

and this is the part of the event that it should control the expiration:

    private async createTaskToExpireInvite(invite: Invite) {
        const expiresAt = invite.expiresAt;

        if (expiresAt.getTime() <= Date.now()) {
            console.warn("Invite is already expired — skipping schedule.");
            return;
        }

        const checkInvitationExpiryTask = checkInvitationExpiry(
            this.inviteTaskService,
        );

        await checkInvitationExpiryTask.trigger({
            scheduleId
        });
    }

tech stack: Nest.js, GraphQL, Typeorm

queen zincBOT
#

Hi @twin egret. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

To implement your use case—expiring an invite at a specific time using Trigger.dev—the best approach is to use a regular task with the delay option, not a scheduled (cron) task. Scheduled tasks are for recurring jobs, while the delay option is designed for one-off, time-based executions like yours. Here’s how you can do it:

1. Define a Regular Task

You should define your task using task() (not schedules.task()), since you want to trigger it once per invite, at the invite's expiresAt time.

import { task } from "@trigger.dev/sdk/v3";
import { UniqueEntityID } from "@vtal/common/domain/unique-entity-id.vo";
import { InviteTaskService } from "@/modules/invites/application/services/invite-task.service";

export function checkInvitationExpiry(taskService: InviteTaskService) {
  return task({
    id: "check-invitation-expiry",
    run: async (payload) => {
      await taskService.expireInvite(new UniqueEntityID(payload.inviteId));
    },
  });
}

2. Trigger the Task with a Delay

When the invite is created, trigger the task with the delay option set to the expiresAt time. The payload should include the inviteId (or whatever identifier you use).

private async createTaskToExpireInvite(invite: Invite) {
  const expiresAt = invite.expiresAt;

  if (expiresAt.getTime() <= Date.now()) {
    console.warn("Invite is already expired — skipping schedule.");
    return;
  }

  const checkInvitationExpiryTask = checkInvitationExpiry(this.inviteTaskService);

  await checkInvitationExpiryTask.trigger(
    { inviteId: invite.id.toString() },
    { delay: expiresAt } // You can pass a Date object directly
  );
}
#
  • The delay option accepts a Date object, an ISO string, or a duration string (e.g., "1h"). This will schedule the task to run at the exact expiration time of the invite.
  • The task will be executed once, at the specified time, and will check the invite status as you want.

References:

3. Why Not Use Scheduled Tasks?

  • Scheduled tasks (schedules.task()) are for recurring jobs (e.g., "run every day at 8am"). They require a cron schedule and are not intended for one-off, per-invite timing.
  • Regular tasks with the delay option are perfect for your use case: trigger once, at a specific time in the future.

Summary:

  • Use a regular task (task()), not a scheduled task.
  • Trigger it with the delay option set to the invite's expiresAt.
  • Pass the invite ID in the payload so your task can look up and expire the invite if needed.

If you need to cancel or reschedule the expiration (e.g., if the invite is accepted before expiresAt), you can use the run handle returned by trigger() and the runs.cancel or runs.reschedule SDK functions as described in the docs.

Let me know if you need a full code example or further clarification!

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@twin egret Kapa.ai is still learning and improving, please let me know how I did by reacting below.

twin egret
#

@queen zinc ok, but for my task I have this issue:

➜  api git:(develop) ✗ npx trigger.dev@latest dev

Trigger.dev (3.3.17)
------------------------------------------------------
Key: Version | Task | Run
------------------------------------------------------
○ Building background worker…
│
■  Error: No tasks exported from your trigger files
│  
│         Files:
│         src/shared/lib/trigger/expire-invite-task.ts
│         
│         Make sure you have at least one task exported from your trigger files.
│         
│         You may have defined a task and forgot to add the export statement:
│         
│         ```ts
│         import { task } from "@trigger.dev/sdk/v3";
│         
│         👇 Don't forget this
│         export const myTask = task({
│           id: "myTask",
│           async run() {
│             // Your task logic here
│           }
│         });
│         ```
│  
│         View the task docs

queen zincBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The error message you’re seeing:

Error: No tasks exported from your trigger files
...
Make sure you have at least one task exported from your trigger files.

means that Trigger.dev requires at least one task to be exported from your trigger files so it can discover and register them. In your current code, you are returning the task from a function, but not exporting it at the top level of the file.

How to fix:
You need to export your task directly from the file, not just return it from a function. For example:

import { task } from "@trigger.dev/sdk/v3";
import { UniqueEntityID } from "@vtal/common/domain/unique-entity-id.vo";
import { InviteTaskService } from "@/modules/invites/application/services/invite-task.service";
import { inviteTaskService } from "@/modules/invites/application/services/invite-task.service.instance"; // however you instantiate it

export const checkInvitationExpiry = task({
  id: "check-invitation-expiry",
  run: async (payload) => {
    await inviteTaskService.expireInvite(new UniqueEntityID(payload.inviteId));
  },
});
  • Make sure the exported task is a top-level export in your trigger directory (e.g., /trigger/expire-invite-task.ts).
  • If you need to inject dependencies, instantiate them outside and use them in the exported task.

Why?
Trigger.dev scans your trigger files for exported tasks to register them. If you only define tasks inside functions and don’t export them, they won’t be discovered, and you’ll get this error. This is explicitly mentioned in the tasks overview documentation.

You must export each task, even subtasks inside the same file. When exported they are accessible so their configuration can be registered with the platform.

#

Note:
If you are using Trigger.dev v4 or above, the requirement to export tasks has changed, but for v3 (as in your code), you must export them. If you are on v4, you can define tasks without exporting, but for v3, exporting is required Tasks: Overview Hidden tasks in v4.


Summary:
Export your task at the top level of your trigger file so Trigger.dev can find and register it. This will resolve the error you’re seeing.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@twin egret Kapa.ai is still learning and improving, please let me know how I did by reacting below.

twin egret
#

@queen zinc now this is happening:

@vtal/api:dev: /Users/gouvea/www/vtal/vtal-api/node_modules/@trigger.dev/core/dist/commonjs/v3/apiClientManager/index.js:46
@vtal/api:dev:             throw new ApiClientMissingError(this.apiClientMissingError());
@vtal/api:dev:                   ^
@vtal/api:dev: 
@vtal/api:dev: ApiClientMissingError: You need to set the TRIGGER_SECRET_KEY environment variable. See https://trigger.dev/docs/management/overview#authentication
@vtal/api:dev:     at APIClientManagerAPI.clientOrThrow (/Users/gouvea/www/vtal/vtal-api/node_modules/@trigger.dev/core/src/v3/apiClientManager/index.ts:57:13)
@vtal/api:dev:     at trigger_internal (/Users/gouvea/www/vtal/vtal-api/node_modules/@trigger.dev/sdk/src/v3/shared.ts:1186:38)
@vtal/api:dev:     at Object.trigger (/Users/gouvea/www/vtal/vtal-api/node_modules/@trigger.dev/sdk/src/v3/shared.ts:134:20)
@vtal/api:dev:     at createTaskToExpireInvite (/Users/gouvea/www/vtal/vtal-api/apps/api/src/modules/invites/application/event-handlers/on-invite-created.ts:46:31)
@vtal/api:dev:     at OnInviteCreated.executeEvent (/Users/gouvea/www/vtal/vtal-api/apps/api/src/modules/invites/application/event-handlers/on-invite-created.ts:95:14)
@vtal/api:dev:     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
@vtal/api:dev: 
@vtal/api:dev: Node.js v18.20.4

as i am using monorepo, I have: /apps/api/.env
inside .env I have the secret:

# TRIGGER
TRIGGER_SECRET_KEY=tr_dev....

I also tried this:

private async createTaskToExpireInvite(invite: Invite) {
        const expiresAt = invite.expiresAt;
        console.log("creationtaskshouldbehere", JSON.stringify(invite));

        if (expiresAt.getTime() <= Date.now()) {
            console.warn("Invite is already expired — skipping schedule.");
            return;
        }

        configure({
            accessToken: process.env.TRIGGER_SECRET_KEY,
        });
        await checkInvitationExpiry.trigger(
            {
                taskService: this.inviteTaskService,
                inviteId: invite.id,
            },
            { delay: expiresAt },
        );
    }

but error persists

queen zincBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The error message:

ApiClientMissingError: You need to set the TRIGGER_SECRET_KEY environment variable. See <https://trigger.dev/docs/management/overview#authentication>

means that the Trigger.dev SDK cannot find the TRIGGER_SECRET_KEY environment variable at runtime.

Key points from the docs:

  • The SDK will automatically use the value of the TRIGGER_SECRET_KEY environment variable if it is set.
  • If you are using the SDK in a different environment, you can set the key using the configure function, but the property should be secretKey, not accessToken for secret keys (use accessToken only for personal access tokens) API keys Management API Authentication.

Troubleshooting steps:

  1. Check the property name in configure:
    You should use secretKey, not accessToken, when configuring with a secret key.

    configure({
      secretKey: process.env.TRIGGER_SECRET_KEY,
    });
    
  2. Environment variable loading:
    Make sure your environment variable is actually loaded in the process where the Trigger.dev worker runs. In a monorepo, if you have /apps/api/.env, ensure your process is started from the /apps/api directory or that your environment loader (like dotenv) is configured to load from the correct path.

  3. Where to set the variable:
    The docs recommend setting TRIGGER_SECRET_KEY in your .env file (e.g., .env.local for Next.js App Router, .env for Pages Router) Next.js setup guide.

#
  1. If using the CLI:
    When running npx trigger.dev@latest dev, ensure that the CLI process has access to the environment variable. You may need to run the CLI from the directory where .env is located, or explicitly set the variable in your shell:

    TRIGGER_SECRET_KEY=tr_dev_... npx trigger.dev@latest dev
    

Summary of what to change in your code:

Replace:

configure({
    accessToken: process.env.TRIGGER_SECRET_KEY,
});

with:

configure({
    secretKey: process.env.TRIGGER_SECRET_KEY,
});

If the error persists, double-check that the environment variable is available to the process running the Trigger.dev worker. You can add a console.log(process.env.TRIGGER_SECRET_KEY) before calling configure to verify it's loaded.

If you follow these steps and the variable is set correctly, the error should be resolved Management API Overview.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@twin egret Kapa.ai is still learning and improving, please let me know how I did by reacting below.

twin egret
#

@queen zinc yeah but by using secretKey I have this message:

@deprecated — Use accessToken instead
queen zincBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Thank you for pointing out the deprecation warning regarding secretKey. According to the official Trigger.dev documentation, when authenticating with the management API, you should use secretKey for environment-specific secret keys (those starting with tr_dev_, tr_prod_, etc.), and accessToken for personal access tokens (those starting with tr_pat_). The documentation examples for environment variable management and most management API endpoints still use secretKey for project/environment keys, not accessToken Authentication.

Here’s a direct quote from the docs:

Our TypeScript SDK will default to using the value of the TRIGGER_SECRET_KEY environment variable if it is set. If you are using the SDK in a different environment, you can set the key using the configure function.

import { configure } from "@trigger.dev/sdk/v3";

configure({ accessToken: "tr_dev_1234" });

Delete Env Var

However, in the code example above, the docs do show accessToken being used with a tr_dev_ key, which is inconsistent with the rest of the documentation and the authentication table. The Authentication page clearly states:

  • Use secretKey for environment/project keys (tr_dev_, tr_prod_, etc.)
  • Use accessToken for personal access tokens (tr_pat_)

If you are seeing a deprecation warning for secretKey, it may be due to a recent SDK update, but the official documentation still recommends using secretKey for environment keys. If the SDK now expects accessToken for all keys (including environment keys), this is not yet reflected in the documentation.

#

In summary:

  • The knowledge sources show both usages, but the main documentation still recommends secretKey for environment keys.
  • If you see a deprecation warning, and the SDK works with accessToken for your tr_dev_... key, you can use it as shown in the docs' code snippets.
  • If you encounter issues, refer to the Authentication page for the most up-to-date guidance.

If you need further clarification, I recommend checking the SDK release notes or reaching out to the Trigger.dev team, as the documentation currently contains both patterns and may be in transition.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@twin egret Kapa.ai is still learning and improving, please let me know how I did by reacting below.

twin egret
#

@queen zinc worked, but now this...

@vtal/api:dev: /Users/gouvea/www/vtal/vtal-api/node_modules/@trigger.dev/core/dist/commonjs/v3/apiClient/index.js:79
@vtal/api:dev:             body: JSON.stringify(body),
@vtal/api:dev:                        ^
@vtal/api:dev: 
@vtal/api:dev: TypeError: Converting circular structure to JSON
@vtal/api:dev:     --> starting at object with constructor 'EntityMetadata'
@vtal/api:dev:     |     property 'ownColumns' -> object with constructor 'Array'
@vtal/api:dev:     |     index 0 -> object with constructor 'ColumnMetadata'
@vtal/api:dev:     --- property 'entityMetadata' closes the circle
@vtal/api:dev:     at JSON.stringify (<anonymous>)
@vtal/api:dev:     at ApiClient.triggerTask (/Users/gouvea/www/vtal/vtal-api/node_modules/@trigger.dev/core/src/v3/apiClient/index.ts:203:20)
@vtal/api:dev:     at trigger_internal (/Users/gouvea/www/vtal/vtal-api/node_modules/@trigger.dev/sdk/src/v3/shared.ts:1192:34)
@vtal/api:dev:     at Object.trigger (/Users/gouvea/www/vtal/vtal-api/node_modules/@trigger.dev/sdk/src/v3/shared.ts:134:14)
@vtal/api:dev:     at OnInviteCreated.createTaskToExpireInvite (/Users/gouvea/www/vtal/vtal-api/apps/api/src/modules/invites/application/event-handlers/on-invite-created.ts:46:3)
@vtal/api:dev:     at OnInviteCreated.executeEvent (/Users/gouvea/www/vtal/vtal-api/apps/api/src/modules/invites/application/event-handlers/on-invite-created.ts:95:3)
@vtal/api:dev: 
@vtal/api:dev: Node.js v18.20.4

what does it mean

queen zincBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The error message:

TypeError: Converting circular structure to JSON
...
at JSON.stringify (<anonymous>)
...

means that you are trying to pass an object with a circular reference as the payload to your Trigger.dev task. The Trigger.dev SDK serializes the payload using JSON.stringify, which cannot handle circular references.

What does this mean in your context?

  • You are likely passing a complex object (such as an instance of a service, repository, or ORM entity) as part of the payload to your task.

  • In your earlier code, you had:

    await checkInvitationExpiry.trigger(
        {
            taskService: this.inviteTaskService,
            inviteId: invite.id,
        },
        { delay: expiresAt },
    );
    

    Here, taskService: this.inviteTaskService is problematic. Service instances (and most class instances) often contain circular references and cannot be serialized to JSON.

How to fix:

  • Only pass plain data (like strings, numbers, booleans, or plain objects without methods or circular references) as the payload to your Trigger.dev tasks.
  • Do not pass class instances, services, or ORM entities.

Correct usage:

await checkInvitationExpiry.trigger(
    { inviteId: invite.id }, // Only pass the ID or other simple data
    { delay: expiresAt }
);

Then, in your task, use the ID to look up the entity or perform the action you need.

This approach is consistent with the Trigger.dev documentation, which shows passing only simple, JSON-serializable objects as payloads to tasks Type-safe metadata.


#

Summary:
You must only pass JSON-serializable data (no class instances or services) as the payload to your Trigger.dev tasks. Passing complex objects with circular references will cause this error.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@twin egret Kapa.ai is still learning and improving, please let me know how I did by reacting below.

twin egret
#

@queen zinc ok but then by removing the taskService from the payload, how am I suppose to execute the service to expire the invite if necessary?

queen zincBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

You should not pass the service instance (taskService) in the payload. Instead, you should instantiate or inject the service inside your task file, and only pass simple, serializable data (like the inviteId) in the payload.

Here’s how you can structure it:

  1. Export your task and instantiate the service at the top level of the file:
import { task } from "@trigger.dev/sdk/v3";
import { UniqueEntityID } from "@vtal/common/domain/unique-entity-id.vo";
import { InviteTaskService } from "@/modules/invites/application/services/invite-task.service";
import { invitesRepository } from "@/modules/invites/application/repositories/invite.repository"; // however you instantiate it

const inviteTaskService = new InviteTaskService(invitesRepository);

export const checkInvitationExpiry = task({
  id: "check-invitation-expiry",
  run: async (payload) => {
    await inviteTaskService.expireInvite(new UniqueEntityID(payload.inviteId));
  },
});
  1. Trigger the task with only the data needed:
await checkInvitationExpiry.trigger(
  { inviteId: invite.id },
  { delay: expiresAt }
);

Why?
Trigger.dev tasks are designed to receive only JSON-serializable payloads. The task logic itself should handle any service or repository instantiation or injection needed to perform its work. This pattern is shown in the Trigger.dev documentation, where only simple data is passed to the task, and any required services are used inside the task definition.

If you need to use dependency injection (e.g., with NestJS), you may need to adapt your architecture so that the task file can access the necessary services, either by importing them directly or by using a singleton pattern.

#

Summary:

  • Only pass simple, serializable data (like IDs) in the payload.
  • Instantiate or import your service inside the task file, not in the payload.

This approach is required by Trigger.dev’s design and is consistent with the official documentation.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@twin egret Kapa.ai is still learning and improving, please let me know how I did by reacting below.

twin egret
#

@queen zinc I cannot instantiate the service because it is a injectable service from nest.js

queen zincBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I'm sorry, but the knowledge sources do not contain explicit information about integrating NestJS dependency injection (such as using @Injectable services) directly within Trigger.dev tasks. The documentation and examples provided focus on passing only serializable data (like IDs) to tasks and do not address how to use framework-specific dependency injection (like NestJS's) inside a Trigger.dev task's run function.

The recommended approach in the docs is to instantiate or import any required services at the top level of the task file, and only pass simple data in the payload. There is also a note in the troubleshooting section that advises not to use NestJS inside your tasks, as NestJS can swallow errors and interfere with Trigger.dev's error handling:

Our current recommendation is to not use NestJS inside your tasks. If you’re a NestJS user you can still use Trigger.dev but just don’t use NestJS inside your tasks like this.
Common problems: Framework specific issues

If you need to use business logic from a NestJS service, you may need to refactor that logic into a plain function or module that can be imported and used in your Trigger.dev task, rather than relying on NestJS's dependency injection within the task itself.

If you need more advanced integration between NestJS and Trigger.dev, the knowledge sources do not provide a documented solution.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@twin egret Kapa.ai is still learning and improving, please let me know how I did by reacting below.

twin egret
#

@queen zinc I fixed by doing this:

import { task } from "@trigger.dev/sdk/v3";
import { UniqueEntityID } from "@vtal/common/domain/unique-entity-id.vo";
import { InviteTaskService } from "@/modules/invites/application/services/invite-task.service";
import { NestFactory } from "@nestjs/core";
import { AppModule } from "@/app.module";

export const checkInvitationExpiry = task({
    id: "check-invitation-expiry",
    run: async (payload: { inviteId: string }) => {
        const app = await NestFactory.createApplicationContext(AppModule, {
            logger: ["log", "error", "warn", "debug"],
        });

        try {
            const taskService = app.get(InviteTaskService);

            if (!taskService) {
                throw new Error(
                    "InviteTaskService is undefined — not resolved from app context",
                );
            }

            await taskService.expireInvite(new UniqueEntityID(payload.inviteId));
        } catch (e) {
            console.error("Task execution error:", e);
            throw e;
        } finally {
            await app.close();
        }
    },
});

but now trigger is fialing to execute the task:

check-invitation-expiry


Overview
o

Detail
d

Context
c

Metadata
m
Failed

Triggered
May 24, 12:25:17.146 AM
59 seconds

Dequeued
12:26:16.187 AM
460 milliseconds

Started
12:26:16.647 AM
4.9 seconds

Finished
12:26:21.555 AM
TypeError
Cannot read properties of undefined (reading 'expireInvite')

TypeError: Cannot read properties of undefined (reading 'expireInvite')
at run (file:///Users/gouvea/www/vtal/vtal-api/apps/api/src/shared/lib/trigger/expire-invite-task.ts:11:29)
Payload




{
  "inviteId": "a9a8e6f1-b848-43df-b1ee-e55fcee2fb40"
}

these are the logs from trigger:

2025-05-24T03:26:16.824Z check-invitation-expiry TRACE handleError() (0.3 milliseconds)
2025-05-24T03:26:16.820Z check-invitation-expiry TRACE Attempt 1 (118 milliseconds)
2025-05-24T03:26:16.937Z check-invitation-expiry exception Cannot read properties of undefined (reading 'expireInvite')
2025-05-24T03:26:18.118Z check-invitation-expiry TRACE Retry #1 delay (112.9 milliseconds)
2025-05-24T03:26:18.672Z check-invitation-expiry TRACE handleError() (0.3 milliseconds)
2025-05-24T03:26:18.667Z check-invitation-expiry TRACE Attempt 2 (113.9 milliseconds)
2025-05-24T03:26:18.781Z check-invitation-expiry exception Cannot read properties of undefined (reading 'expireInvite')
2025-05-24T03:26:19.411Z check-invitation-expiry TRACE Retry #2 delay (1.4 seconds)
2025-05-24T03:26:20.835Z check-invitation-expiry TRACE handleError() (0.3 milliseconds)
2025-05-24T03:26:20.831Z check-invitation-expiry TRACE Attempt 3 (115 milliseconds)
2025-05-24T03:26:20.946Z check-invitation-expiry exception Cannot read properties of undefined (reading 'expireInvite')
2025-05-24T03:25:17.140Z check-invitation-expiry TRACE check-invitation-expiry (1 minute, 4.4 seconds)
2025-05-24T03:26:21.555Z check-invitation-expiry exception Cannot read properties of undefined (reading 'expireInvite')

What am i suppose to do

queen zincBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Based on the knowledge sources, using NestJS dependency injection (NestFactory.createApplicationContext(AppModule)) inside a Trigger.dev task is not recommended and can lead to issues. The Trigger.dev documentation explicitly warns that using NestJS inside your tasks can swallow errors and prevent proper error handling:

Our current recommendation is to not use NestJS inside your tasks. If you’re a NestJS user you can still use Trigger.dev but just don’t use NestJS inside your tasks like this.

Your error (Cannot read properties of undefined (reading 'expireInvite')) suggests that app.get(InviteTaskService) is returning undefined. This is likely because the NestJS application context is not being properly initialized in the isolated environment where Trigger.dev runs your task, or the service is not available in that context.

What should you do?

  • Do not use NestJS dependency injection inside Trigger.dev tasks.
  • Refactor the business logic you need (e.g., the code to expire an invite) into a plain function or module that does not depend on NestJS's DI system.
  • Import and use that plain function directly in your Trigger.dev task, passing only serializable data (like the invite ID) as the payload.

This approach is consistent with the official guidance and avoids the issues you are experiencing. If you need to share logic between your NestJS services and Trigger.dev tasks, extract that logic into a shared module that both can use, but do not rely on NestJS's DI within the task itself.

#

If you need more advanced integration, the knowledge sources do not provide a documented solution for using NestJS DI inside Trigger.dev tasks. The recommended and supported pattern is to keep Trigger.dev tasks framework-agnostic and only use plain JavaScript/TypeScript code inside them Common problems: Framework specific issues.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: