musistudio/claude-code-router · warning

[deep-link] Failed to register

Error message

[deep-link] Failed to register ${appDeepLinkProtocol} protocol: ${formatError(error)}

What it means

Electron's app.setAsDefaultProtocolClient failed while registering the app's custom deep-link protocol. The call throws on some platforms when registry access (Windows) or LaunchServices registration (macOS) fails, and the wrapper logs it instead of crashing.

Solutions

  1. Run the app from a proper build/installer where process.defaultApp is false
  2. Verify the protocol name is unique and not registered by another application
  3. On Windows, check HKCU\Software\Classes\<protocol> manually and delete stale entries
  4. Wrap registration in a retry after app 'ready' if currently called before it

Example fix

// before
app.setAsDefaultProtocolClient(appDeepLinkProtocol);
// after
if (app.isReady()) {
  app.setAsDefaultProtocolClient(appDeepLinkProtocol);
} else {
  app.whenReady().then(() => app.setAsDefaultProtocolClient(appDeepLinkProtocol));
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!app.isReady()) { /* defer registration */ }

Type guard

const isProtocolName = (v: string): v is string => /^[a-z][a-z0-9+.-]*$/.test(v);

Try / catch

try { app.setAsDefaultProtocolClient(proto); } catch (e) { console.warn(`register failed: ${formatError(e)}`); }

Prevention

When it happens

Trigger: Calling register() during app startup on Windows without admin rights, a sandboxed/dev environment where HKCU registry writes fail, or a protocol already claimed by another app. Also fires when process.defaultApp path handling passes a bad argv[1] in dev mode.

Common situations: Running unpackaged (electron .) where execPath/argv differ, corporate machines with registry policies, macOS during CI without a proper bundle id, or antivirus blocking registry writes.

Related errors


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

Appendix: source

Thrown at packages/electron/src/main/deep-link.ts:181

        dialog.showErrorBox("Failed to open CCR plugin app", detail);
      } catch {
        // The console error above remains available when the dialog API is unavailable.
      }
      windowsManager.showMainWindow();
    } finally {
      this.openingPluginRequests.delete(requestKey);
    }
  }

  private registerProtocolClient(): void {
    try {
      if (process.defaultApp && process.argv.length >= 2) {
        app.setAsDefaultProtocolClient(appDeepLinkProtocol, process.execPath, [path.resolve(process.argv[1])]);
        return;
      }
      app.setAsDefaultProtocolClient(appDeepLinkProtocol);
    } catch (error) {
      console.warn(`[deep-link] Failed to register ${appDeepLinkProtocol} protocol: ${formatError(error)}`);
    }
  }
}

async function ensurePluginAppUrlAvailable(config: AppConfig, appUrl: string, startedGateway: boolean): Promise<void> {
  try {
    await waitForPluginAppUrl(
      appUrl,
      startedGateway ? pluginAppStartupProbeTimeoutMs : pluginAppExistingGatewayProbeTimeoutMs
    );
    return;
  } catch (error) {
    if (startedGateway) {
      throw error;
    }
    console.warn(`[deep-link] Plugin app URL was not reachable on the running gateway; restarting gateway once. ${formatError(error)}`);
  }

View on GitHub (pinned to 99f24806c6)