Skip to content

bug: CLI crashes with "Invalid comparator: catalog:" when @trigger.dev/* deps use workspace catalogs (bun/pnpm catalog: protocol) #3905

@JoeRall

Description

@JoeRall

Provide environment information

System:
OS: Windows 11 10.0.26200
CPU: (8) x64 Intel(R) Core(TM) Ultra 7 258V
Memory: 9.90 GB / 31.55 GB
Binaries:
Node: 22.20.0
npm: 10.9.3
bun: 1.3.14
Deno: 2.7.14

Describe the bug

Running trigger dev (or deploy, preview, workers build — anything that runs the embedded update check) crashes immediately when @trigger.dev/* dependencies in package.json use the catalog: version protocol (bun and pnpm workspace catalogs):

TypeError: Invalid comparator: catalog:
    at Comparator.parse (.../semver/classes/comparator.js:41:13)
    ...
    at Module.minVersion (.../semver/ranges/min-version.js:8:11)
    at .../trigger.dev/dist/esm/commands/update.js:71:42

Reproduction repo

See Repo Info Below

To reproduce

In a bun (or pnpm) workspace, define a catalog in the root package.json and reference it from the app that runs the CLI:

// root package.json
"workspaces": {
  "packages": ["apps/*"],
  "catalog": {
    "@trigger.dev/sdk": "4.4.6",
    "trigger.dev": "4.4.6"
  }
}
// apps/trigger/package.json
"dependencies": {
  "@trigger.dev/sdk": "catalog:"
},
"devDependencies": {
  "trigger.dev": "catalog:"
}

Then run trigger dev.

Additional information

getTriggerDependencies() in packages/cli-v3/src/commands/update.ts explicitly skips workspace: specifiers, but has no equivalent skip for catalog::

if (version.startsWith("workspace")) {
continue;
}

When the tryResolveTriggerPackageVersion() fallback fails to resolve the installed version from node_modules, the raw "catalog:" string is kept as dep.version and flows into semver.minVersion() in getVersionMismatches(), which throws (it's wrapped in neither a try/catch nor a semver.validRange guard):

const depMinVersion = semver.minVersion(dep.version);

Suggested fix

Skip catalog: the same way workspace: is skipped:

if (version.startsWith("workspace") || version.startsWith("catalog:")) {
  continue;
}

(Optionally also harden getVersionMismatches() — e.g. guard the semver.minVersion() call with semver.validRange(dep.version) or a try/catch — so any other non-semver specifier degrades to a warning instead of a crash.)

Note: skipping these specifiers entirely does mean catalog users lose the version-mismatch check. A nicer fix would be to keep relying on tryResolveTriggerPackageVersion() (which already resolves the real installed version) and only skip when resolution fails — but never letting catalog: reach semver is the minimal correct change.

Workaround

trigger dev --skip-update-check (and the same flag on deploy) bypasses the crashing code path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions