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

  1. Run the feature inside CCR Desktop.
  2. Remove/disable the desktop-only plugin entry when running the CLI runtime.
  3. 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

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


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/10694a123dfef8ba. Report an issue: GitHub.