Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/max-compute-seconds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@trigger.dev/core": patch
"@trigger.dev/sdk": patch
"trigger.dev": patch
---

Add `maxComputeSeconds` as the user-facing replacement for `maxDuration` on `defineConfig`, task definitions, and trigger options. The new name makes the unit (compute-time seconds) unambiguous at the call site. `maxDuration` is JSDoc-deprecated and still accepted; if both are set, `maxComputeSeconds` wins. The `init` templates have been updated to use the new name.
9 changes: 7 additions & 2 deletions packages/cli-v3/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,11 +359,16 @@ function validateConfig(config: TriggerConfig, warn = true) {
config.build.extensions.push(adaptResolveEnvVarsToSyncEnvVarsExtension(resolveEnvVarsFn));
}

if (!config.maxDuration) {
// Resolve maxComputeSeconds → maxDuration so plain-object exports that bypass
// defineConfig() still work. This mirrors the resolution defineConfig() applies
// at the SDK boundary; downstream CLI/runtime code only reads `maxDuration`.
const resolvedMaxDuration = config.maxComputeSeconds ?? config.maxDuration;
if (!resolvedMaxDuration) {
throw new Error(
`The "maxDuration" trigger.config option is now required, and must be at least 5 seconds.`
`The "maxComputeSeconds" trigger.config option is now required, and must be at least 5 seconds.`
);
}
config.maxDuration = resolvedMaxDuration;
Comment on lines +362 to +371

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Project-wide compute limit is silently ignored for config files that don't use the helper wrapper

The project-wide compute limit written under the new name is only converted to the old name inside the command-line tool's copy of the settings (config.maxDuration = resolvedMaxDuration at packages/cli-v3/src/config.ts:371), not in the separate copy the task-indexing step loads, so tasks get no limit at all.

Impact: Users who export their settings as a plain object with only the new option get runs with no project-level compute cap, even though the tool reported the setting as valid.

Why the CLI-side mutation does not reach the indexed task manifests

validateConfig runs only inside the CLI process (packages/cli-v3/src/config.ts:196). The index workers re-import the user's built config module directly (importConfig at packages/cli-v3/src/entryPoints/dev-index-worker.ts:75-86, same in packages/cli-v3/src/entryPoints/managed-index-worker.ts) and then apply the project default with if (typeof config.maxDuration === "number") (packages/cli-v3/src/entryPoints/dev-index-worker.ts:147-152). For a config exported through defineConfig() the resolution happens at import time in the SDK, so it works; for a plain-object export — exactly the case the new comment at packages/cli-v3/src/config.ts:362-364 claims to support — the worker sees only maxComputeSeconds, maxDuration is undefined, and no per-task default is applied.

Prompt for agents
The CLI's validateConfig now resolves maxComputeSeconds into maxDuration by mutating its in-process copy of the config (packages/cli-v3/src/config.ts). However the dev and managed index workers (packages/cli-v3/src/entryPoints/dev-index-worker.ts and managed-index-worker.ts) import the user's built trigger.config module directly via importConfig() and read config.maxDuration to apply the project-level default to every task manifest. For configs exported as plain objects (bypassing defineConfig, which is the case the new comment says it supports), the worker's copy has only maxComputeSeconds, so the project-level limit is silently dropped. Consider resolving maxComputeSeconds -> maxDuration in the index workers too (or in a shared helper both the CLI and the entrypoints use) so the fallback works for every code path that reads the config.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


if (config.runtime && config.runtime === "bun") {
warn &&
Expand Down
4 changes: 2 additions & 2 deletions packages/cli-v3/templates/examples/schedule.mjs.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ export const firstScheduledTask = schedules.task({
id: "first-scheduled-task",
// Every hour
cron: "0 * * * *",
// Set an optional maxDuration to prevent tasks from running indefinitely
maxDuration: 300, // Stop executing after 300 secs (5 mins) of compute
// Set an optional maxComputeSeconds to prevent tasks from running indefinitely
maxComputeSeconds: 300, // Stop executing after 300 secs (5 mins) of compute
run: async (payload, { ctx }) => {
// The payload contains the last run timestamp that you can use to check if this is the first run
// And calculate the time since the last run
Expand Down
4 changes: 2 additions & 2 deletions packages/cli-v3/templates/examples/schedule.ts.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ export const firstScheduledTask = schedules.task({
id: "first-scheduled-task",
// Every hour
cron: "0 * * * *",
// Set an optional maxDuration to prevent tasks from running indefinitely
maxDuration: 300, // Stop executing after 300 secs (5 mins) of compute
// Set an optional maxComputeSeconds to prevent tasks from running indefinitely
maxComputeSeconds: 300, // Stop executing after 300 secs (5 mins) of compute
run: async (payload, { ctx }) => {
// The payload contains the last run timestamp that you can use to check if this is the first run
// And calculate the time since the last run
Expand Down
4 changes: 2 additions & 2 deletions packages/cli-v3/templates/examples/simple.mjs.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { logger, task, wait } from "@trigger.dev/sdk/v3";

export const helloWorldTask = task({
id: "hello-world",
// Set an optional maxDuration to prevent tasks from running indefinitely
maxDuration: 300, // Stop executing after 300 secs (5 mins) of compute
// Set an optional maxComputeSeconds to prevent tasks from running indefinitely
maxComputeSeconds: 300, // Stop executing after 300 secs (5 mins) of compute
run: async (payload, { ctx }) => {
logger.log("Hello, world!", { payload, ctx });

Expand Down
4 changes: 2 additions & 2 deletions packages/cli-v3/templates/examples/simple.ts.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { logger, task, wait } from "@trigger.dev/sdk/v3";

export const helloWorldTask = task({
id: "hello-world",
// Set an optional maxDuration to prevent tasks from running indefinitely
maxDuration: 300, // Stop executing after 300 secs (5 mins) of compute
// Set an optional maxComputeSeconds to prevent tasks from running indefinitely
maxComputeSeconds: 300, // Stop executing after 300 secs (5 mins) of compute
run: async (payload: any, { ctx }) => {
logger.log("Hello, world!", { payload, ctx });

Expand Down
4 changes: 2 additions & 2 deletions packages/cli-v3/templates/trigger.config.mjs.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ export default defineConfig({
project: "${projectRef}",
runtime: "${runtime}",
logLevel: "log",
// The max compute seconds a task is allowed to run. If the task run exceeds this duration, it will be stopped.
// The max compute seconds a task is allowed to run. If the run exceeds this, it will be stopped.
// You can override this on an individual task.
// See https://trigger.dev/docs/runs/max-duration
maxDuration: 3600,
maxComputeSeconds: 3600,
retries: {
enabledInDev: true,
default: {
Expand Down
4 changes: 2 additions & 2 deletions packages/cli-v3/templates/trigger.config.ts.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ export default defineConfig({
project: "${projectRef}",
runtime: "${runtime}",
logLevel: "log",
// The max compute seconds a task is allowed to run. If the task run exceeds this duration, it will be stopped.
// The max compute seconds a task is allowed to run. If the run exceeds this, it will be stopped.
// You can override this on an individual task.
// See https://trigger.dev/docs/runs/max-duration
maxDuration: 3600,
maxComputeSeconds: 3600,
retries: {
enabledInDev: true,
default: {
Expand Down
14 changes: 13 additions & 1 deletion packages/core/src/v3/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,28 @@ export type TriggerConfig = {
*/
logLevel?: LogLevel;

/**
* The maximum duration in compute-time **seconds** that a task run is allowed to run. If the task run exceeds this duration, it will be stopped.
*
* Minimum value is 5 seconds.
*
* Setting this value will affect all tasks in the project. You can override it on a per-task basis.
*
* @see https://trigger.dev/docs/tasks/overview#maxcomputeseconds-option
*/
maxComputeSeconds?: number;

/**
* The maximum duration in compute-time seconds that a task run is allowed to run. If the task run exceeds this duration, it will be stopped.
*
* Minimum value is 5 seconds
*
* Setting this value will effect all tasks in the project.
*
* @deprecated Use `maxComputeSeconds` instead — same semantics, clearer unit. If both are set, `maxComputeSeconds` wins.
* @see https://trigger.dev/docs/tasks/overview#maxduration-option
*/
maxDuration: number;
maxDuration?: number;

/**
* Set a default time-to-live (TTL) for all task runs in the project. If a run is not executed within this time, it will be removed from the queue and never execute.
Expand Down
10 changes: 9 additions & 1 deletion packages/core/src/v3/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,9 @@ export class MaxDurationExceededError extends Error {
public readonly maxDurationInSeconds: number,
public readonly elapsedTimeInSeconds: number
) {
super(`Run exceeded maximum compute time (maxDuration) of ${maxDurationInSeconds} seconds`);
super(
`Run exceeded maximum compute time (maxComputeSeconds) of ${maxDurationInSeconds} seconds`
);

this.name = "MaxDurationExceededError";
}
Expand Down Expand Up @@ -715,6 +717,12 @@ const prettyInternalErrors: Partial<
href: links.docs.machines.home,
},
},
MAX_DURATION_EXCEEDED: {
link: {
name: "How to set maxComputeSeconds (maxDuration)",
href: links.docs.maxDuration,
},
},
TASK_PROCESS_MAYBE_OOM_KILLED: {
message:
"Your run was terminated due to exceeding the machine's memory limit. Try increasing the machine preset in your task options or replay using a larger machine.",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/v3/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const links = {
personalAccessToken:
"https://trigger.dev/docs/github-actions#creating-a-personal-access-token",
},
maxDuration: "https://trigger.dev/docs/runs/max-duration",
},
site: {
home: "https://trigger.dev",
Expand Down
20 changes: 20 additions & 0 deletions packages/core/src/v3/types/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,19 @@ type CommonTaskOptions<
}
| MachinePresetName;

/**
* The maximum duration in compute-time **seconds** that a task run is allowed to run. If the task run exceeds this duration, it will be stopped.
*
* Minimum value is 5 seconds.
*/
maxComputeSeconds?: number;

/**
* The maximum duration in compute-time seconds that a task run is allowed to run. If the task run exceeds this duration, it will be stopped.
*
* Minimum value is 5 seconds
*
* @deprecated Use `maxComputeSeconds` instead — same semantics, clearer unit. If both are set, `maxComputeSeconds` wins.
*/
maxDuration?: number;

Expand Down Expand Up @@ -887,12 +896,23 @@ export type TriggerOptions = {
*/
metadata?: Record<string, SerializableJson>;

/**
* The maximum duration in compute-time **seconds** that a task run is allowed to run. If the task run exceeds this duration, it will be stopped.
*
* This will override the task's `maxComputeSeconds` (or the legacy `maxDuration`).
*
* Minimum value is 5 seconds.
*/
maxComputeSeconds?: number;

/**
* The maximum duration in compute-time seconds that a task run is allowed to run. If the task run exceeds this duration, it will be stopped.
*
* This will override the task's maxDuration.
*
* Minimum value is 5 seconds
*
* @deprecated Use `maxComputeSeconds` instead — same semantics, clearer unit. If both are set, `maxComputeSeconds` wins.
*/
maxDuration?: number;

Expand Down
29 changes: 29 additions & 0 deletions packages/trigger-sdk/src/v3/config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, it, expect } from "vitest";
import { defineConfig } from "./config.js";

describe("defineConfig - maxComputeSeconds", () => {
it("uses maxComputeSeconds when only maxComputeSeconds is set", () => {
const cfg = defineConfig({ project: "p", maxComputeSeconds: 600 });
expect(cfg.maxDuration).toBe(600);
});

it("uses maxDuration when only maxDuration is set", () => {
const cfg = defineConfig({ project: "p", maxDuration: 600 });
expect(cfg.maxDuration).toBe(600);
});

it("prefers maxComputeSeconds when both are set", () => {
const cfg = defineConfig({ project: "p", maxComputeSeconds: 600, maxDuration: 9999 });
expect(cfg.maxDuration).toBe(600);
});

it("leaves maxDuration unset when neither is provided", () => {
const cfg = defineConfig({ project: "p" });
expect(cfg.maxDuration).toBeUndefined();
});

it("strips maxComputeSeconds from the returned config", () => {
const cfg = defineConfig({ project: "p", maxComputeSeconds: 600 });
expect((cfg as { maxComputeSeconds?: number }).maxComputeSeconds).toBeUndefined();
});
});
10 changes: 9 additions & 1 deletion packages/trigger-sdk/src/v3/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ export type {
} from "@trigger.dev/core/v3";

export function defineConfig(config: TriggerConfig): TriggerConfig {
return config;
// `maxComputeSeconds` is the new name for `maxDuration`. If both are set, the new
// name wins. Internally the SDK and platform still read `maxDuration`, so we
// collapse the two fields here at the user-facing boundary.
const { maxComputeSeconds, maxDuration, ...rest } = config;
const resolved = maxComputeSeconds ?? maxDuration;
return {
...rest,
...(resolved !== undefined ? { maxDuration: resolved } : {}),
};
}

export type { TriggerConfig };
20 changes: 20 additions & 0 deletions packages/trigger-sdk/src/v3/maxComputeSeconds.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, it, expect } from "vitest";
import { resolveMaxComputeSeconds } from "./maxComputeSeconds.js";

describe("resolveMaxComputeSeconds", () => {
it("returns maxComputeSeconds when only maxComputeSeconds is set", () => {
expect(resolveMaxComputeSeconds({ maxComputeSeconds: 300 })).toBe(300);
});

it("returns maxDuration when only maxDuration is set", () => {
expect(resolveMaxComputeSeconds({ maxDuration: 300 })).toBe(300);
});

it("prefers maxComputeSeconds when both are set", () => {
expect(resolveMaxComputeSeconds({ maxComputeSeconds: 300, maxDuration: 999 })).toBe(300);
});

it("returns undefined when neither is set", () => {
expect(resolveMaxComputeSeconds({})).toBeUndefined();
});
});
13 changes: 13 additions & 0 deletions packages/trigger-sdk/src/v3/maxComputeSeconds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Collapse the user-facing `maxComputeSeconds` (new name) and `maxDuration` (deprecated)
* into a single value. If both are provided, `maxComputeSeconds` wins.
*
* Internal SDK/CLI/platform code only reads `maxDuration`, so all call sites that
* accept user input should funnel through this helper before forwarding the value.
*/
export function resolveMaxComputeSeconds(input: {
maxComputeSeconds?: number;
maxDuration?: number;
}): number | undefined {
return input.maxComputeSeconds ?? input.maxDuration;
}
Loading