musistudio/claude-code-router · error · Error
${productName} is only available in CCR Desktop.
Error message
${productName} is only available in CCR Desktop. What it means
withClaudeProductRuntimePluginConfig refuses to install a bundled Claude product runtime plugin outside the desktop app: isDesktopAppRuntime() must be true. Guard-clauses a plugin that only ships in the Electron build.
Source
Thrown at packages/core/src/config/config.ts:2990
surfaces: knownGatewayPluginDefaultSurfaces(pluginId)
};
}
export function withClaudeDesignRuntimePluginConfig(config: AppConfig): AppConfig {
return withClaudeProductRuntimePluginConfig(config, CLAUDE_DESIGN_PLUGIN_ID, "Claude Design");
}
export function withClaudeShipRuntimePluginConfig(config: AppConfig): AppConfig {
return withClaudeProductRuntimePluginConfig(config, CLAUDE_SHIP_PLUGIN_ID, "Claude Ship");
}
function withClaudeProductRuntimePluginConfig(config: AppConfig, pluginId: string, productName: string): AppConfig {
const existingIndex = config.plugins.findIndex((plugin) => plugin.enabled !== false && plugin.id === pluginId);
if (existingIndex >= 0 && config.plugins[existingIndex]?.module?.trim()) {
return config;
}
if (!isDesktopAppRuntime()) {
throw new Error(`${productName} is only available in CCR Desktop.`);
}
const plugin = claudeProductRuntimePluginConfig(pluginId);
if (!plugin) {
throw new Error(`${productName} runtime module was not found. Rebuild app assets so the bundled ${productName} plugin is copied into the Electron dist.`);
}
if (existingIndex >= 0) {
const existing = config.plugins[existingIndex];
const plugins = [...config.plugins];
plugins[existingIndex] = {
...plugin,
...existing,
apps: existing.apps ?? plugin.apps,
module: plugin.module,
permissions: existing.permissions ?? plugin.permissions,
surfaces: existing.surfaces ?? plugin.surfaces
};
return {
...config,View on GitHub (pinned to 99f24806c6)
Solutions
- Run the feature inside CCR Desktop.
- Remove/disable the desktop-only plugin entry when running the CLI runtime.
- Gate config per-runtime instead of sharing one config file.
Example fix
// before
config = withClaudeProductRuntimePluginConfig(config, pluginId, name);
// after
if (isDesktopAppRuntime()) {
config = withClaudeProductRuntimePluginConfig(config, pluginId, name);
} else {
config = disablePlugin(config, pluginId);
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!isDesktopAppRuntime()) skipDesktopOnlyPlugin(pluginId);
Type guard
function canUseClaudeProductRuntime(): boolean { return isDesktopAppRuntime(); } Try / catch
catch (e) { if ((e as Error).message.endsWith('only available in CCR Desktop.')) disablePluginAndWarn(pluginId); } Prevention
- Keep desktop-only plugins out of CLI/server configs.
- Branch config by runtime before enabling bundled plugins.
When it happens
Trigger: Enabling a Claude product plugin (e.g. via config) while running under the CLI/server runtime instead of CCR Desktop.
Common situations: Same config reused on a server/CLI install, or a test environment loading desktop-only config.
Related errors
- ${productName} runtime module was not found. Rebuild app ass
- No Bot Gateway conversationRef is configured and no inbound
- Kimi CLI provider configuration was not found.
- ZCode provider API key was not found in ZCode config.
- Refusing to archive unknown legacy JSON config file: ${unsup
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/10694a123dfef8ba.
Report an issue: GitHub.