can1357/oh-my-pi · error · Error

Could not resolve ${APP_NAME} launcher path in PATH

Error message

Could not resolve ${APP_NAME} launcher path in PATH

What it means

A defensive guard in the bun/npm self-update branch: when a forced binary takeover is requested but the update target's launcher filesystem path is unknown, the code refuses to proceed rather than overwrite an unknown file. The comment notes this is only reachable on Windows, where a script launcher resolved from PATH may lack a recorded path because bin-dir probes are skipped in forced mode.

Source

Thrown at packages/coding-agent/src/cli/update-cli.ts:2015

		const target = await resolveUpdateTarget({ allowPackageManagers: !forceBinary });
		if (channel === "canary" && (target.method === "nix" || target.method === "brew" || target.method === "mise")) {
			console.log(chalk.yellow("Canary updates are only supported for bun, npm, or binary installs."));
			return;
		}
		if (target.method === "nix") {
			console.log(chalk.yellow("This installation is managed by Nix and cannot update itself."));
			console.log(chalk.dim("Update the flake input or profile that provides omp, then rebuild."));
			return;
		} else if (target.method === "brew") {
			await updateViaHomebrew(release.version, opts.force);
		} else if (target.method === "mise") {
			await updateViaMise(release.version, opts.force);
		} else if (target.method === "bun" || target.method === "npm") {
			if (forceBinary) {
				// Reachable in forced mode only through a Windows script
				// launcher resolved from PATH (the bun/npm bin-dir probes are
				// skipped), so the launcher path is always known.
				if (!target.path) throw new Error(`Could not resolve ${APP_NAME} launcher path in PATH`);
				console.log(chalk.dim("This release ships as a standalone binary; replacing the script launcher."));
				await updateViaShimTakeover(target.path, release.version, { allowPrerelease });
				console.log(
					chalk.yellow(
						`This install is no longer managed by ${target.method}. Removing the old global package may delete this launcher; if it does, reinstall with: ${installerHint()}`,
					),
				);
			} else {
				await updateViaManager(
					release,
					target.path,
					packageManagerUpdateSteps(target.method, release, allowPrerelease),
				);
			}
		} else {
			if (forceBinary && target.replacesSymlink) {
				console.log(chalk.dim("Replacing the package-manager launcher with the standalone binary."));
			}

View on GitHub (pinned to 9690622007)

Solutions

  1. Run the update without forced-binary mode so the normal probe path resolves the launcher
  2. Locate the launcher manually (`where omp` / `Get-Command omp` in PowerShell) and install the standalone binary over it yourself
  3. Reinstall via bun/npm so the launcher sits in a standard bin dir the probes can find
  4. Check PATH for non-standard entries and normalize them before updating

Example fix

// before (crashes on unknown path)
if (!target.path) throw new Error(`Could not resolve ${APP_NAME} launcher path in PATH`);
await updateViaShimTakeover(target.path, release.version, { allowPrerelease });
// after (resolve path explicitly first)
const launcherPath = target.path ?? (await resolveLauncherFromPath(APP_NAME));
if (!launcherPath) throw new Error(`Could not resolve ${APP_NAME} launcher path in PATH`);
await updateViaShimTakeover(launcherPath, release.version, { allowPrerelease });
Defensive patterns

Strategy: validation

Validate before calling

import { $which } from "@oh-my-pi/pi-utils";
const launcher = await $which(APP_NAME);
if (!launcher) throw new Error(`${APP_NAME} not found on PATH; cannot force binary takeover`);

Type guard

function hasLauncherPath(t: UpdateTarget): t is UpdateTarget & { path: string } {
	return typeof t.path === "string" && t.path.length > 0;
}

Try / catch

try {
	await updateSelf({ forceBinary: true });
} catch (err) {
	if (err.message.includes("Could not resolve") || err.message.includes("launcher path")) {
		// resolve launcher manually (`where omp`) and reinstall standalone binary
	} else throw err;
}

Prevention

When it happens

Trigger: Running forced self-update (--force-binary style) on a bun/npm install where target.method is bun or npm and target.path is undefined — practically only on Windows when the launcher was found via PATH lookup instead of the bun/npm bin-dir probes.

Common situations: Windows installs where the shim lives in a directory outside the detected bun/npm bin dirs (custom PATH entry, alternate install location); heavily customized PATH setups.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/f2f23de06fd463d5. Report an issue: GitHub.