musistudio/claude-code-router · warning
[deep-link] Failed to register ${appDeepLinkProtocol} protoc
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.
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)
Solutions
- Run the app from a proper build/installer where process.defaultApp is false
- Verify the protocol name is unique and not registered by another application
- On Windows, check HKCU\Software\Classes\<protocol> manually and delete stale entries
- 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
- Register protocols only after app ready
- Use a unique protocol scheme
- Test in packaged builds, not just dev mode
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
- ZCode profiles can only open the app; agent arguments are no
- Claude App profiles do not support agent arguments.
- Failed to open Claude App: ${spawnError}
- Failed to open ZCode App: ${spawnError}
- Unable to load @the-next-ai/bot-gateway-sdk. ${errors.join("
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/c391cb136e29837f.
Report an issue: GitHub.