musistudio/claude-code-router · error · Error
Failed to open Claude App: ${spawnError}
Error message
Failed to open Claude App: ${spawnError} What it means
Thrown by installUpdate when there is no downloaded update ready to install — status.canInstall is false, meaning the download step never completed (or was not run) on this staging path.
Source
Thrown at packages/cli/src/cli.ts:193
const runtimeResult = applyProfileRuntimeConfig(launchConfig, profile, launchConfig.APIKEY);
if (!runtimeResult.ok) {
throw new Error(runtimeResult.message);
}
}
if (resolvedSurface === "cli" && process.env[prepareProfileOnlyEnv] === "1") {
return;
}
if (profile.agent === "claude-code" && resolvedSurface === "app") {
applyClaudeAppGatewayConfig(launchConfig);
applyClaudeAppGatewayConfig(launchConfig, {
backup: false,
dataDir: resolveClaudeAppProfileUserDataDir(configDir, profile),
refreshModelDiscoveryCache: true
});
const launch = await launchClaudeAppProfile(configDir, profile, launchConfig);
const spawnError = await waitForImmediateSpawnError(launch.child, 500);
if (spawnError) {
throw new Error(`Failed to open Claude App: ${spawnError}`);
}
process.stdout.write(`Opened Claude App with ${profile.name || profile.id}.\n`);
return;
}
if (profile.agent === "zcode" && resolvedSurface === "app" && profileOptions.agentArgs.length === 0) {
const launch = launchZcodeAppProfile(configDir, profile, launchConfig);
const spawnError = await waitForImmediateSpawnError(launch.child, 500);
if (spawnError) {
throw new Error(`Failed to open ZCode App: ${spawnError}`);
}
process.stdout.write(`Opened ZCode App with ${profile.name || profile.id}.\n`);
return;
}
const plan = buildProfileLaunchPlan(configDir, profile, resolvedSurface, profileOptions.agentArgs);
if (path.isAbsolute(plan.command) && !existsSync(plan.command)) {
throw new Error(`Profile launcher was not found: ${plan.command}. Open CCR once or re-save the profile.`);View on GitHub (pinned to 99f24806c6)
Solutions
- Ensure downloadUpdate() awaited successfully before enabling/calling installUpdate()
- Subscribe to update status and only offer install when canInstall is true
- If download previously failed, re-run downloadUpdate() and handle its error first
- After app restart, re-check for updates and re-download before installing
Example fix
// before
await updates.installUpdate(); // no staged download
// after
await updates.downloadUpdate();
if ((await updates.getStatus()).canInstall) {
await updates.installUpdate();
} Defensive patterns
Strategy: validation
Validate before calling
const status = await updateService.getStatus();
if (!status.canInstall) {
await updateService.downloadUpdate();
}
await updateService.installUpdate(); Type guard
const canInstallUpdate = (s: UpdateStatus): boolean => s.canInstall === true;
Try / catch
try {
await updateService.installUpdate();
} catch (error) {
if (error instanceof Error && /No downloaded update/.test(error.message)) {
await updateService.downloadUpdate(); // then retry install
} else { throw error; }
} Prevention
- Gate install UI on status.canInstall, not on download initiation
- Await downloadUpdate before enabling the install action
- Re-check status after app restart — staged updates don't persist across sessions
When it happens
Trigger: Calling installUpdate() before downloadUpdate() has succeeded, after a failed download, or after the staged update was cleared/restarted.
Common situations: UI flow bug that skips or races the download step; user clicks 'install' while download is still in progress; app restarted between download and install so no staged update exists; a prior download failed silently.
Related errors
- Failed to open ZCode App: ${spawnError}
- Claude App profiles do not support agent arguments.
- ZCode profiles can only open the app; agent arguments are no
- Unable to load @the-next-ai/bot-gateway-sdk. ${errors.join("
- ${productName} runtime module was not found. Rebuild app ass
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/b2d387cd9190c155.
Report an issue: GitHub.