{"record":{"id":"10694a123dfef8ba","repo":"musistudio/claude-code-router","slug":"productname-is-only-available-in-ccr-desktop","errorCode":null,"errorMessage":"${productName} is only available in CCR Desktop.","messagePattern":"(.+?) is only available in CCR Desktop\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/config/config.ts","lineNumber":2990,"sourceCode":"    surfaces: knownGatewayPluginDefaultSurfaces(pluginId)\n  };\n}\n\nexport function withClaudeDesignRuntimePluginConfig(config: AppConfig): AppConfig {\n  return withClaudeProductRuntimePluginConfig(config, CLAUDE_DESIGN_PLUGIN_ID, \"Claude Design\");\n}\n\nexport function withClaudeShipRuntimePluginConfig(config: AppConfig): AppConfig {\n  return withClaudeProductRuntimePluginConfig(config, CLAUDE_SHIP_PLUGIN_ID, \"Claude Ship\");\n}\n\nfunction withClaudeProductRuntimePluginConfig(config: AppConfig, pluginId: string, productName: string): AppConfig {\n  const existingIndex = config.plugins.findIndex((plugin) => plugin.enabled !== false && plugin.id === pluginId);\n  if (existingIndex >= 0 && config.plugins[existingIndex]?.module?.trim()) {\n    return config;\n  }\n  if (!isDesktopAppRuntime()) {\n    throw new Error(`${productName} is only available in CCR Desktop.`);\n  }\n  const plugin = claudeProductRuntimePluginConfig(pluginId);\n  if (!plugin) {\n    throw new Error(`${productName} runtime module was not found. Rebuild app assets so the bundled ${productName} plugin is copied into the Electron dist.`);\n  }\n  if (existingIndex >= 0) {\n    const existing = config.plugins[existingIndex];\n    const plugins = [...config.plugins];\n    plugins[existingIndex] = {\n      ...plugin,\n      ...existing,\n      apps: existing.apps ?? plugin.apps,\n      module: plugin.module,\n      permissions: existing.permissions ?? plugin.permissions,\n      surfaces: existing.surfaces ?? plugin.surfaces\n    };\n    return {\n      ...config,","sourceCodeStart":2972,"sourceCodeEnd":3008,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/config/config.ts#L2972-L3008","documentation":"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.","triggerScenarios":"Enabling a Claude product plugin (e.g. via config) while running under the CLI/server runtime instead of CCR Desktop.","commonSituations":"Same config reused on a server/CLI install, or a test environment loading desktop-only config.","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."],"exampleFix":"// before\nconfig = withClaudeProductRuntimePluginConfig(config, pluginId, name);\n// after\nif (isDesktopAppRuntime()) {\n  config = withClaudeProductRuntimePluginConfig(config, pluginId, name);\n} else {\n  config = disablePlugin(config, pluginId);\n}","handlingStrategy":"type-guard","validationCode":"if (!isDesktopAppRuntime()) skipDesktopOnlyPlugin(pluginId);","typeGuard":"function canUseClaudeProductRuntime(): boolean { return isDesktopAppRuntime(); }","tryCatchPattern":"catch (e) { if ((e as Error).message.endsWith('only available in CCR Desktop.')) disablePluginAndWarn(pluginId); }","preventionTips":["Keep desktop-only plugins out of CLI/server configs.","Branch config by runtime before enabling bundled plugins."],"tags":["plugin","runtime","desktop-only","config"],"backgroundTag":"unsupported-runtime-environment","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}