Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { cn } from "~/utils/cn";
import { docsPath, v3RunSpanPath, v3TaskParamsSchema, v3TestPath } from "~/utils/pathBuilder";
import { TestTaskService } from "~/v3/services/testTask.server";
import { OutOfEntitlementError } from "~/v3/services/triggerTask.server";
import { ServiceValidationError } from "~/v3/services/baseService.server";
import { TestTaskData } from "~/v3/testTask";
import { RunTagInput } from "~/components/runs/v3/RunTagInput";
import { type loader as queuesLoader } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues";
Expand Down Expand Up @@ -237,6 +238,13 @@ export const action: ActionFunction = async ({ request, params }) => {
);
}

if (e instanceof ServiceValidationError) {
return redirectBackWithErrorMessage(
request,
`Unable to start a test run: ${e.message}`
);
}

logger.error("Failed to start a test run", { error: e instanceof Error ? e.message : e });

return redirectBackWithErrorMessage(
Expand Down
53 changes: 41 additions & 12 deletions apps/webapp/app/runEngine/concerns/queues.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export class DefaultQueueManager implements QueueManager {
): Promise<QueueProperties> {
let queueName: string;
let lockedQueueId: string | undefined;
let resolvedRegion: string | undefined;

// Determine queue name based on lockToVersion and provided options
if (lockedBackgroundWorker) {
Expand All @@ -83,6 +84,17 @@ export class DefaultQueueManager implements QueueManager {
// Use the validated queue name directly
queueName = specifiedQueue.name;
lockedQueueId = specifiedQueue.id;

// Get task-level region config for the locked worker
const lockedTask = await this.prisma.backgroundWorkerTask.findFirst({
where: {
workerId: lockedBackgroundWorker.id,
runtimeEnvironmentId: request.environment.id,
slug: request.taskId,
},
select: { region: true },
});
resolvedRegion = lockedTask?.region ?? undefined;
} else {
// No specific queue name provided, use the default queue for the task on the locked worker
const lockedTask = await this.prisma.backgroundWorkerTask.findFirst({
Expand Down Expand Up @@ -121,6 +133,7 @@ export class DefaultQueueManager implements QueueManager {
// Use the task's default queue name
queueName = lockedTask.queue.name;
lockedQueueId = lockedTask.queue.id;
resolvedRegion = lockedTask.region ?? undefined;
}
} else {
// Task is not locked to a specific version, use regular logic
Expand All @@ -131,8 +144,10 @@ export class DefaultQueueManager implements QueueManager {
);
}

// Get queue name using the helper for non-locked case (handles provided name or finds default)
queueName = await this.getQueueName(request);
// Get queue name and task-level region using the helper for non-locked case
const queueInfo = await this._getQueueInfo(request);
queueName = queueInfo.queueName;
resolvedRegion = queueInfo.region ?? undefined;
}

// Sanitize the final determined queue name once
Expand All @@ -148,19 +163,21 @@ export class DefaultQueueManager implements QueueManager {
return {
queueName,
lockedQueueId,
resolvedRegion,
};
}

async getQueueName(request: TriggerTaskRequest): Promise<string> {
const { queueName } = await this._getQueueInfo(request);
return queueName;
}
Comment on lines 170 to +173
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all callers of getQueueName
echo "=== Callers of getQueueName ==="
rg -n "getQueueName" --type=ts -A3 -B3

echo -e "\n=== sanitizeQueueName usage and definition ==="
rg -n "sanitizeQueueName" --type=ts -C3

echo -e "\n=== Check the full queues.server.ts file ==="
wc -l apps/webapp/app/runEngine/concerns/queues.server.ts

Repository: triggerdotdev/trigger.dev

Length of output: 14904


🏁 Script executed:

#!/bin/bash
# Check if getQueueName is actually being called by searching more broadly
echo "=== Direct getQueueName calls ==="
rg "getQueueName\(" --type=ts

echo -e "\n=== Check queues.server.ts context around getQueueName ==="
sed -n '165,180p' apps/webapp/app/runEngine/concerns/queues.server.ts

echo -e "\n=== Check if QueuesConcern is instantiated and getQueueName used ==="
rg "\.getQueueName\(" --type=ts -B2 -A2

echo -e "\n=== Check imports of QueuesConcern ==="
rg "QueuesConcern" --type=ts | head -20

echo -e "\n=== Look for resolveQueueProperties which is similar ==="
rg "resolveQueueProperties\(" --type=ts -B2 -A2 | head -40

Repository: triggerdotdev/trigger.dev

Length of output: 2489


🏁 Script executed:

#!/bin/bash
# Check if runEngine is exported as public API
echo "=== Check if runEngine types are exported ==="
find . -name "index.ts" -o -name "index.d.ts" | xargs grep -l "runEngine\|QueueManager" 2>/dev/null

echo -e "\n=== Check package.json exports ==="
rg '"exports"' --type=json -A 30 | head -50

echo -e "\n=== Look for getQueueName in entire repo, including comments ==="
rg "getQueueName" --type=ts --type=js

echo -e "\n=== Check when getQueueName was added/modified ==="
git log --oneline -n 20 -- apps/webapp/app/runEngine/concerns/queues.server.ts

echo -e "\n=== Check the actual implementation of _getQueueInfo ==="
sed -n '175,240p' apps/webapp/app/runEngine/concerns/queues.server.ts

Repository: triggerdotdev/trigger.dev

Length of output: 5998


getQueueName returns an unsanitized queue name while resolveQueueProperties sanitizes—creating inconsistent behavior for the same interface.

Both methods extract queue info via _getQueueInfo, but resolveQueueProperties applies sanitizeQueueName (lines 154–161) while getQueueName returns the raw result directly (lines 170–173). Since _getQueueInfo can return user-provided or raw database queue names without sanitization, callers of getQueueName may receive unsanitized names containing spaces or special characters, while resolveQueueProperties callers receive sanitized names—causing mismatches with stored queues.

🔧 Suggested fix — apply the same sanitization as `resolveQueueProperties`
  async getQueueName(request: TriggerTaskRequest): Promise<string> {
    const { queueName } = await this._getQueueInfo(request);
-   return queueName;
+   const sanitized = sanitizeQueueName(queueName);
+   return sanitized || sanitizeQueueName(`task/${request.taskId}`) || `task/${request.taskId}`;
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async getQueueName(request: TriggerTaskRequest): Promise<string> {
const { queueName } = await this._getQueueInfo(request);
return queueName;
}
async getQueueName(request: TriggerTaskRequest): Promise<string> {
const { queueName } = await this._getQueueInfo(request);
const sanitized = sanitizeQueueName(queueName);
return sanitized || sanitizeQueueName(`task/${request.taskId}`) || `task/${request.taskId}`;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/runEngine/concerns/queues.server.ts` around lines 170 - 173,
getQueueName currently returns the raw queueName from _getQueueInfo while
resolveQueueProperties sanitizes it; update getQueueName to call
sanitizeQueueName on the queueName returned by _getQueueInfo and return the
sanitized value so both interfaces behave consistently with
resolveQueueProperties (use the same sanitizeQueueName utility and any
trimming/normalization logic applied there).


private async _getQueueInfo(
request: TriggerTaskRequest
): Promise<{ queueName: string; region?: string | null }> {
const { taskId, environment, body } = request;
const { queue } = body.options ?? {};

// Use extractQueueName to handle double-wrapped queue objects
const queueName = extractQueueName(queue);
if (queueName) {
return queueName;
}

const defaultQueueName = `task/${taskId}`;

// Find the current worker for the environment
Expand All @@ -172,7 +189,9 @@ export class DefaultQueueManager implements QueueManager {
environmentId: environment.id,
});

return defaultQueueName;
// Use extractQueueName to handle double-wrapped queue objects
const queueName = extractQueueName(queue);
return { queueName: queueName ?? defaultQueueName };
}

const task = await this.prisma.backgroundWorkerTask.findFirst({
Expand All @@ -192,7 +211,14 @@ export class DefaultQueueManager implements QueueManager {
environmentId: environment.id,
});

return defaultQueueName;
const queueName = extractQueueName(queue);
return { queueName: queueName ?? defaultQueueName };
}

// Use specified queue name if provided, otherwise fall back to task's queue
const specifiedQueueName = extractQueueName(queue);
if (specifiedQueueName) {
return { queueName: specifiedQueueName, region: task.region };
}

if (!task.queue) {
Expand All @@ -202,10 +228,13 @@ export class DefaultQueueManager implements QueueManager {
queueConfig: task.queueConfig,
});

return defaultQueueName;
return { queueName: defaultQueueName, region: task.region };
}

return task.queue.name ?? defaultQueueName;
return {
queueName: task.queue.name ?? defaultQueueName,
region: task.region,
};
}

async validateQueueLimits(
Expand Down
14 changes: 9 additions & 5 deletions apps/webapp/app/runEngine/services/triggerTask.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,11 @@ export class RunEngineTriggerTaskService {
})
: undefined;

const { queueName, lockedQueueId } = await this.queueConcern.resolveQueueProperties(
triggerRequest,
lockedToBackgroundWorker ?? undefined
);
const { queueName, lockedQueueId, resolvedRegion } =
await this.queueConcern.resolveQueueProperties(
triggerRequest,
lockedToBackgroundWorker ?? undefined
);

//upsert tags
const tags = await createTags(
Expand All @@ -284,7 +285,10 @@ export class RunEngineTriggerTaskService {

const depth = parentRun ? parentRun.depth + 1 : 0;

const workerQueue = await this.queueConcern.getWorkerQueue(environment, body.options?.region);
// Trigger-time region overrides task-level region, which overrides project default
const effectiveRegion = body.options?.region ?? resolvedRegion;

const workerQueue = await this.queueConcern.getWorkerQueue(environment, effectiveRegion);

try {
return await this.traceEventConcern.traceRun(
Expand Down
1 change: 1 addition & 0 deletions apps/webapp/app/runEngine/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type QueueValidationResult =
export type QueueProperties = {
queueName: string;
lockedQueueId?: string;
resolvedRegion?: string;
};

export type LockedBackgroundWorker = Pick<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ async function createWorkerTask(
retryConfig: task.retry,
queueConfig: task.queue,
machineConfig: task.machine,
region: task.region,
triggerSource: task.triggerSource === "schedule" ? "SCHEDULED" : "STANDARD",
fileId: tasksToBackgroundFiles?.get(task.id) ?? null,
maxDurationInSeconds: task.maxDuration ? clampMaxDuration(task.maxDuration) : null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "BackgroundWorkerTask" ADD COLUMN "region" TEXT;
1 change: 1 addition & 0 deletions internal-packages/database/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@ model BackgroundWorkerTask {
queueConfig Json?
retryConfig Json?
machineConfig Json?
regionConfig String?
Comment on lines 559 to +562
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

regionConfig type inconsistency with other *Config fields may block future evolution.

All adjacent *Config fields (queueConfig, retryConfig, machineConfig) are Json?. regionConfig is String?. This works for the current string-only region name, but per issue #3017 the region option should eventually support an object form { name: string, fallback: boolean }. Storing as String? will require a migration (or a parallel boolean column) if the fallback property is added later.

Consider using Json? now (storing the region name as a JSON string, e.g., "us-east-1") so the column type is consistent and flexible, avoiding a future breaking migration.

🗄️ Proposed schema alternative
-  regionConfig  String?
+  regionConfig  Json?

And correspondingly in createBackgroundWorker.server.ts:

-  regionConfig: task.region,
+  regionConfig: task.region ?? null,

(No other change needed since Prisma will accept a string as a JSON value.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal-packages/database/prisma/schema.prisma` around lines 559 - 562,
Change the Prisma column type for regionConfig from String? to Json? so it
matches queueConfig, retryConfig, and machineConfig and allows future expansion
to an object like { name: string, fallback: boolean }; update any code that
reads/writes regionConfig (e.g., createBackgroundWorker.server.ts) to continue
supporting plain string values by storing/reading a JSON string or JSON value
(Prisma will accept a string as JSON), and run the migration to alter the column
type accordingly.


queueId String?
queue TaskQueue? @relation(fields: [queueId], references: [id], onDelete: SetNull, onUpdate: Cascade)
Expand Down
14 changes: 14 additions & 0 deletions packages/cli-v3/src/entryPoints/dev-index-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,20 @@ if (typeof config.machine === "string") {
});
}

// If the config has a region, we need to apply it to all tasks that don't have a region
if (typeof config.region === "string") {
tasks = tasks.map((task) => {
if (typeof task.region !== "string") {
return {
...task,
region: config.region,
} satisfies TaskManifest;
}

return task;
});
}

await sendMessageInCatalog(
indexerToWorkerMessages,
"INDEX_COMPLETE",
Expand Down
14 changes: 14 additions & 0 deletions packages/cli-v3/src/entryPoints/managed-index-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,20 @@ if (typeof config.machine === "string") {
});
}

// If the config has a region, we need to apply it to all tasks that don't have a region
if (typeof config.region === "string") {
tasks = tasks.map((task) => {
if (typeof task.region !== "string") {
return {
...task,
region: config.region,
} satisfies TaskManifest;
}

return task;
});
}

const processKeepAlive = config.processKeepAlive ?? config.experimental_processKeepAlive;

await sendMessageInCatalog(
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/v3/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ export type TriggerConfig = {
*/
machine?: MachinePresetName;

/**
* The default region to run your tasks in. You can override this on a per-task basis or at trigger time.
*
* Check the Regions page in the dashboard for regions that are available to you.
*/
region?: string;
Comment on lines +144 to +149
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if .changeset directory exists and list its contents
if [ -d ".changeset" ]; then
  echo "=== Changeset files ===" 
  find .changeset -type f -name "*.md" | grep -v "CHANGELOG" | head -20
else
  echo "No .changeset directory found"
fi

Repository: triggerdotdev/trigger.dev

Length of output: 480


🏁 Script executed:

# Check the structure of packages/core to understand the scope
echo "=== Checking packages/core directory ==="
ls -la packages/core/src/v3/ 2>/dev/null || echo "v3 directory not found"

Repository: triggerdotdev/trigger.dev

Length of output: 4430


🏁 Script executed:

# Search for any changeset files that mention core, trigger-sdk, or version changes
echo "=== Searching changeset content ==="
if [ -d ".changeset" ]; then
  find .changeset -type f -name "*.md" ! -name "CHANGELOG*" -exec grep -l "core\|sdk\|region\|config" {} \;
fi

Repository: triggerdotdev/trigger.dev

Length of output: 366


🏁 Script executed:

# Check the content of changesets that matched the search
echo "=== Changeset contents ===" 
for file in .changeset/add-debounce-maxdelay.md .changeset/afraid-gorillas-jump.md .changeset/consistent-stream-targets.md .changeset/export-start-attempt-hook-type.md .changeset/tricky-suits-design.md .changeset/vendor-superjson-esm-fix.md .changeset/vercel-integration.md; do
  if [ -f "$file" ]; then
    echo ""
    echo "--- File: $file ---"
    cat "$file"
  fi
done

Repository: triggerdotdev/trigger.dev

Length of output: 3210


🏁 Script executed:

# Also check if there are any other changesets we might have missed
echo "=== All changeset files (non-README) ==="
find .changeset -type f -name "*.md" ! -name "README.md" -exec basename {} \;

Repository: triggerdotdev/trigger.dev

Length of output: 366


🏁 Script executed:

# Check the remaining changesets we haven't seen yet
for file in .changeset/calm-hooks-wait.md .changeset/fix-dead-process-execute-hang.md .changeset/mcp-wait-timeout.md; do
  if [ -f "$file" ]; then
    echo ""
    echo "--- File: $file ---"
    cat "$file"
  fi
done

Repository: triggerdotdev/trigger.dev

Length of output: 750


🏁 Script executed:

# Now let's check what the actual change is in config.ts to match it with a changeset
echo "=== Checking config.ts file around lines 144-149 ==="
sed -n '140,155p' packages/core/src/v3/config.ts

Repository: triggerdotdev/trigger.dev

Length of output: 661


Add a changeset for the region field addition to TriggerConfig.

The region?: string field added to TriggerConfig in packages/core/src/v3/config.ts is a public API modification that requires a changeset. While several changesets exist for @trigger.dev/core changes in this PR, none cover this specific TriggerConfig modification. Per coding guidelines, run pnpm run changeset:add to create a changeset documenting this API addition.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/v3/config.ts` around lines 144 - 149, The addition of the
optional region?: string field to TriggerConfig is a public API change and needs
a changeset; run pnpm run changeset:add and create a changeset targeting the
`@trigger.dev/core` package that describes the addition of the region field on
TriggerConfig (mark as a minor/feature bump), include a short summary like "Add
optional region to TriggerConfig" and save the generated changeset file so the
release will document this API addition.


/**
* Set the log level for the logger. Defaults to "info", so you will see "log", "info", "warn", and "error" messages, but not "debug" messages.
*
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/v3/schemas/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const TaskResource = z.object({
queue: QueueManifest.extend({ name: z.string().optional() }).optional(),
retry: RetryOptions.optional(),
machine: MachineConfig.optional(),
region: z.string().optional(),
triggerSource: z.string().optional(),
schedule: ScheduleMetadata.optional(),
maxDuration: z.number().optional(),
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/v3/schemas/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ const taskMetadata = {
queue: QueueManifest.extend({ name: z.string().optional() }).optional(),
retry: RetryOptions.optional(),
machine: MachineConfig.optional(),
region: z.string().optional(),
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for changesets in the .changeset directory
ls -la .changeset/ 2>/dev/null | head -20
# List all markdown files in .changeset
fd -e md . .changeset/ 2>/dev/null

Repository: triggerdotdev/trigger.dev

Length of output: 1449


🏁 Script executed:

#!/bin/bash
# Search for changesets mentioning 'core' package
find .changeset -type f -name "*.md" 2>/dev/null -exec grep -l "@trigger.dev/core\|core" {} \;

Repository: triggerdotdev/trigger.dev

Length of output: 210


🏁 Script executed:

#!/bin/bash
# Examine the file to understand the scope of changes
wc -l packages/core/src/v3/schemas/schemas.ts

Repository: triggerdotdev/trigger.dev

Length of output: 112


🏁 Script executed:

#!/bin/bash
# Check the content of changesets that mention core
for file in .changeset/add-debounce-maxdelay.md .changeset/tricky-suits-design.md .changeset/vendor-superjson-esm-fix.md .changeset/vercel-integration.md; do
  echo "=== $file ==="
  cat "$file"
  echo ""
done

Repository: triggerdotdev/trigger.dev

Length of output: 1419


🏁 Script executed:

#!/bin/bash
# Read the schemas.ts file to see the changes
cat -n packages/core/src/v3/schemas/schemas.ts

Repository: triggerdotdev/trigger.dev

Length of output: 11516


🏁 Script executed:

#!/bin/bash
# Search for any changeset mentioning "region"
find .changeset -type f -name "*.md" -exec grep -l "region" {} \;

Repository: triggerdotdev/trigger.dev

Length of output: 51


🏁 Script executed:

#!/bin/bash
# Search for TaskResource and CommonTaskOptions in the schemas file and related files
grep -n "TaskResource\|CommonTaskOptions" packages/core/src/v3/schemas/schemas.ts
# Also search in other schema files
fd -e ts . packages/core/src/v3/ --exec grep -l "TaskResource\|CommonTaskOptions" {} \;

Repository: triggerdotdev/trigger.dev

Length of output: 187


🏁 Script executed:

#!/bin/bash
# Search the entire codebase for TaskResource and CommonTaskOptions
rg "TaskResource|CommonTaskOptions" packages/core --type ts -A 2

Repository: triggerdotdev/trigger.dev

Length of output: 2472


🏁 Script executed:

#!/bin/bash
# Examine TaskResource in resources.ts
cat -n packages/core/src/v3/schemas/resources.ts | head -50

Repository: triggerdotdev/trigger.dev

Length of output: 2124


🏁 Script executed:

#!/bin/bash
# Examine CommonTaskOptions in tasks.ts
wc -l packages/core/src/v3/types/tasks.ts
cat -n packages/core/src/v3/types/tasks.ts | head -100

Repository: triggerdotdev/trigger.dev

Length of output: 3848


🏁 Script executed:

#!/bin/bash
# Find CommonTaskOptions definition in tasks.ts
grep -n "type CommonTaskOptions" packages/core/src/v3/types/tasks.ts -A 30

Repository: triggerdotdev/trigger.dev

Length of output: 1032


🏁 Script executed:

#!/bin/bash
# Get more lines of CommonTaskOptions to see if region is defined
grep -n "type CommonTaskOptions" packages/core/src/v3/types/tasks.ts -A 100 | head -120

Repository: triggerdotdev/trigger.dev

Length of output: 2773


🏁 Script executed:

#!/bin/bash
# Search for "region" specifically in the CommonTaskOptions definition
sed -n '172,400p' packages/core/src/v3/types/tasks.ts | grep -n "region"

Repository: triggerdotdev/trigger.dev

Length of output: 321


🏁 Script executed:

#!/bin/bash
# Get the full CommonTaskOptions type definition
sed -n '172,350p' packages/core/src/v3/types/tasks.ts

Repository: triggerdotdev/trigger.dev

Length of output: 4952


Add changeset for region field addition to @trigger.dev/core public schemas.

The region field has been added to TaskMetadata, TaskManifest, TaskResource, and CommonTaskOptions. As a public package modification, this requires a changeset entry. Run pnpm run changeset:add to document this public API change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/v3/schemas/schemas.ts` at line 189, Public API gained a new
optional "region" string field on TaskMetadata, TaskManifest, TaskResource, and
CommonTaskOptions; add a changeset documenting this public schema change by
running the changeset tool (pnpm run changeset:add) and create a changeset entry
that describes the addition of the optional region field for those
interfaces/types (TaskMetadata, TaskManifest, TaskResource, CommonTaskOptions)
so the `@trigger.dev/core` package version can be updated accordingly.

triggerSource: z.string().optional(),
schedule: ScheduleMetadata.optional(),
maxDuration: z.number().optional(),
Expand Down
19 changes: 19 additions & 0 deletions packages/core/src/v3/types/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,25 @@ type CommonTaskOptions<
*/
maxDuration?: number;

/**
* The default region to run this task in. This can be overridden at trigger time via `options.region`.
*
* Check the Regions page in the dashboard for regions that are available to you.
*
* @example
*
* ```ts
* export const heavyTask = task({
* id: "heavy-task",
* region: "us-east-1",
* run: async ({ payload, ctx }) => {
* // ...
* },
* });
* ```
*/
region?: string;
Comment on lines +280 to +297
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Partial implementation vs. linked issue spec.

Issue #3017 specifies that region should accept either a plain string or { name: string; fallback?: boolean } (to control fallback behaviour when the configured region is removed). The current type is string only; the object form and the fallback mechanism are not implemented here.

If this is a deliberate phase-1 simplification, please add an inline comment or a TODO to document the intent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/v3/types/tasks.ts` around lines 280 - 297, The region
property currently typed as string needs to support the spec's object form;
update the type of the region field (the region property on the task
definition/type in packages/core/src/v3/types/tasks.ts) to accept either string
or { name: string; fallback?: boolean } and ensure any code consuming region
handles both shapes, or if this simplification is intentional add a clear TODO
comment above the region declaration referencing issue `#3017` and stating that
the object form with fallback will be implemented later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ericallam I have renamed this to region instead of regionConfig, do you like to see fallback option or keep this as plain string? currently it is going to throw error that region does not exist.

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!


/** This gets called when a task is triggered. It's where you put the code you want to execute.
*
* @param payload - The payload that is passed to your task when it's triggered. This must be JSON serializable.
Expand Down
2 changes: 2 additions & 0 deletions packages/trigger-sdk/src/v3/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export function createTask<
queue: params.queue,
retry: params.retry ? { ...defaultRetryOptions, ...params.retry } : undefined,
machine: typeof params.machine === "string" ? { preset: params.machine } : params.machine,
region: params.region,
maxDuration: params.maxDuration,
payloadSchema: params.jsonSchema,
fns: {
Expand Down Expand Up @@ -366,6 +367,7 @@ export function createSchemaTask<
queue: params.queue,
retry: params.retry ? { ...defaultRetryOptions, ...params.retry } : undefined,
machine: typeof params.machine === "string" ? { preset: params.machine } : params.machine,
region: params.region,
maxDuration: params.maxDuration,
fns: {
run: params.run,
Expand Down
Loading