fix(cli): point to init when dev or update runs without a project (#3929)

## Summary

Running `trigger.dev dev` before setting up a project crashed with a raw
`Cannot find matching package.json` stack trace from a transitive
dependency, instead of telling the user what to do next. It happens
whenever `dev` (or `update`) runs in a directory with no `package.json`
in it or any parent directory, for example right after creating an empty
project folder, or when `init` was exited before it scaffolded anything.

The CLI now detects the missing project and prints actionable guidance
pointing at `init`.

## Fix

`dev` runs an embedded package-version check before it loads any project
config. That check resolved `package.json` through a helper that throws
when nothing is found up the tree, and nothing caught it. It is now
wrapped, so a missing `package.json` produces a clear "run init" message
and a clean exit.

The config loader had the same latent crash on the `--skip-update-check`
path. Its resolvers for `package.json`, the lockfile, and the workspace
root all ran before the friendly "couldn't find your trigger.config.ts"
check, so any of them throwing masked it. That check now runs first and
short-circuits before the resolvers touch the filesystem.

Verified live: in an empty directory, `dev`, `dev --skip-update-check`,
and `update` all print a "run init" message and exit cleanly; in a
configured project, `dev` still resolves config and boots normally.
This commit is contained in:
Eric Allam
2026-06-12 17:26:13 +01:00
committed by GitHub
parent 43b493628c
commit 3d5cffc255
3 changed files with 44 additions and 15 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Running a CLI command like `dev`, `deploy`, `preview`, or `update` before initializing a project no longer crashes with a raw `Cannot find matching package.json` stack trace. The CLI now detects the missing project and points you to `npx trigger.dev@latest init` instead.
+25 -3
View File
@@ -66,13 +66,35 @@ export async function updateTriggerPackages(
const projectPath = resolve(process.cwd(), dir);
const { packageJson, readonlyPackageJson, packageJsonPath } = await getPackageJson(projectPath);
let packageJsonResult: Awaited<ReturnType<typeof getPackageJson>> | undefined;
try {
packageJsonResult = await getPackageJson(projectPath);
} catch (error) {
// resolvePackageJSON throws when there's no package.json in projectPath or any parent
// directory — usually because the command ran before the project was set up. Don't crash
// with a raw stack trace; fall through to the actionable guidance below.
logger.debug("Failed to resolve package.json for update check", { projectPath, error });
}
if (!packageJsonResult?.packageJson) {
prettyError(
"No package.json found",
`Couldn't find a package.json in ${projectPath} or any parent directory.`,
"Run `npx trigger.dev@latest init` to set up your project, then try again."
);
// When embedded in another command (e.g. `dev`), there's nothing to run without a project,
// so stop here with a clean exit instead of letting the caller fail again downstream.
if (embedded) {
process.exit(1);
}
if (!packageJson) {
log.error("Failed to load package.json. Try to re-run with `-l debug` to see what's going on.");
return false;
}
const { packageJson, readonlyPackageJson, packageJsonPath } = packageJsonResult;
const newCliVersion = await updateCheck();
if (newCliVersion && !cliVersion.startsWith("0.0.0")) {
+14 -12
View File
@@ -152,18 +152,9 @@ async function resolveConfig(
overrides?: Partial<TriggerConfig>,
warn = true
): Promise<ResolvedConfig> {
const packageJsonPath = await resolvePackageJSON(cwd);
const tsconfigPath = await safeResolveTsConfig(cwd);
const lockfilePath = await resolveLockfile(cwd);
const workspaceDir = await findWorkspaceDir(cwd);
const workingDir = result.configFile
? dirname(result.configFile)
: packageJsonPath
? dirname(packageJsonPath)
: cwd;
// `trigger.config` is the fallback value set by c12
// `trigger.config` is the fallback value set by c12. Bail out with actionable guidance before
// touching the filesystem: the pkg-types resolvers below throw raw errors when run outside a
// project (e.g. `dev` before `init`), which would mask this message.
const missingConfigFile = !result.configFile || result.configFile === "trigger.config";
if (missingConfigFile) {
@@ -178,6 +169,17 @@ async function resolveConfig(
);
}
const packageJsonPath = await resolvePackageJSON(cwd);
const tsconfigPath = await safeResolveTsConfig(cwd);
const lockfilePath = await resolveLockfile(cwd);
const workspaceDir = await findWorkspaceDir(cwd);
const workingDir = result.configFile
? dirname(result.configFile)
: packageJsonPath
? dirname(packageJsonPath)
: cwd;
const config =
"config" in result.config ? (result.config.config as TriggerConfig) : result.config;