{"record":{"id":"f2f23de06fd463d5","repo":"can1357/oh-my-pi","slug":"could-not-resolve-app-name-launcher-path-in-pat","errorCode":null,"errorMessage":"Could not resolve ${APP_NAME} launcher path in PATH","messagePattern":"Could not resolve (.+?) launcher path in PATH","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/cli/update-cli.ts","lineNumber":2015,"sourceCode":"\t\tconst target = await resolveUpdateTarget({ allowPackageManagers: !forceBinary });\n\t\tif (channel === \"canary\" && (target.method === \"nix\" || target.method === \"brew\" || target.method === \"mise\")) {\n\t\t\tconsole.log(chalk.yellow(\"Canary updates are only supported for bun, npm, or binary installs.\"));\n\t\t\treturn;\n\t\t}\n\t\tif (target.method === \"nix\") {\n\t\t\tconsole.log(chalk.yellow(\"This installation is managed by Nix and cannot update itself.\"));\n\t\t\tconsole.log(chalk.dim(\"Update the flake input or profile that provides omp, then rebuild.\"));\n\t\t\treturn;\n\t\t} else if (target.method === \"brew\") {\n\t\t\tawait updateViaHomebrew(release.version, opts.force);\n\t\t} else if (target.method === \"mise\") {\n\t\t\tawait updateViaMise(release.version, opts.force);\n\t\t} else if (target.method === \"bun\" || target.method === \"npm\") {\n\t\t\tif (forceBinary) {\n\t\t\t\t// Reachable in forced mode only through a Windows script\n\t\t\t\t// launcher resolved from PATH (the bun/npm bin-dir probes are\n\t\t\t\t// skipped), so the launcher path is always known.\n\t\t\t\tif (!target.path) throw new Error(`Could not resolve ${APP_NAME} launcher path in PATH`);\n\t\t\t\tconsole.log(chalk.dim(\"This release ships as a standalone binary; replacing the script launcher.\"));\n\t\t\t\tawait updateViaShimTakeover(target.path, release.version, { allowPrerelease });\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.yellow(\n\t\t\t\t\t\t`This install is no longer managed by ${target.method}. Removing the old global package may delete this launcher; if it does, reinstall with: ${installerHint()}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait updateViaManager(\n\t\t\t\t\trelease,\n\t\t\t\t\ttarget.path,\n\t\t\t\t\tpackageManagerUpdateSteps(target.method, release, allowPrerelease),\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tif (forceBinary && target.replacesSymlink) {\n\t\t\t\tconsole.log(chalk.dim(\"Replacing the package-manager launcher with the standalone binary.\"));\n\t\t\t}","sourceCodeStart":1997,"sourceCodeEnd":2033,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/cli/update-cli.ts#L1997-L2033","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run the update without forced-binary mode so the normal probe path resolves the launcher","Locate the launcher manually (`where omp` / `Get-Command omp` in PowerShell) and install the standalone binary over it yourself","Reinstall via bun/npm so the launcher sits in a standard bin dir the probes can find","Check PATH for non-standard entries and normalize them before updating"],"exampleFix":"// before (crashes on unknown path)\nif (!target.path) throw new Error(`Could not resolve ${APP_NAME} launcher path in PATH`);\nawait updateViaShimTakeover(target.path, release.version, { allowPrerelease });\n// after (resolve path explicitly first)\nconst launcherPath = target.path ?? (await resolveLauncherFromPath(APP_NAME));\nif (!launcherPath) throw new Error(`Could not resolve ${APP_NAME} launcher path in PATH`);\nawait updateViaShimTakeover(launcherPath, release.version, { allowPrerelease });","handlingStrategy":"validation","validationCode":"import { $which } from \"@oh-my-pi/pi-utils\";\nconst launcher = await $which(APP_NAME);\nif (!launcher) throw new Error(`${APP_NAME} not found on PATH; cannot force binary takeover`);","typeGuard":"function hasLauncherPath(t: UpdateTarget): t is UpdateTarget & { path: string } {\n\treturn typeof t.path === \"string\" && t.path.length > 0;\n}","tryCatchPattern":"try {\n\tawait updateSelf({ forceBinary: true });\n} catch (err) {\n\tif (err.message.includes(\"Could not resolve\") || err.message.includes(\"launcher path\")) {\n\t\t// resolve launcher manually (`where omp`) and reinstall standalone binary\n\t} else throw err;\n}","preventionTips":["On Windows, keep the CLI's shim inside bun/npm's standard bin dir so probes find it","Avoid forcing binary mode when the install origin is ambiguous","Check `Get-Command omp` before forced updates to confirm the launcher location"],"tags":["self-update","windows","path","launcher"],"backgroundTag":"launcher-path-not-found","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}