farion1231/cc-switch · warning · Error
Unsupported URL scheme
Error message
Unsupported URL scheme
What it means
Intended to be thrown by settings.openExternal(url) when the URL parses but its scheme is not http/https (case-insensitive), guarding the Tauri open_external shell call against file:, javascript:, and custom protocols. Quirk: the throw happens inside the same try block whose catch rewrites every failure to 'Invalid URL', so callers currently observe 'Invalid URL' for non-http schemes - this exact message is unreachable as written.
Source
Thrown at src/lib/api/settings.ts:220
return await invoke("s3_sync_fetch_remote_info");
},
async syncCurrentProvidersLive(): Promise<void> {
const result = (await invoke("sync_current_providers_live")) as {
success?: boolean;
message?: string;
};
if (!result?.success) {
throw new Error(result?.message || "Sync current providers failed");
}
},
async openExternal(url: string): Promise<void> {
try {
const u = new URL(url);
const scheme = u.protocol.replace(":", "").toLowerCase();
if (scheme !== "http" && scheme !== "https") {
throw new Error("Unsupported URL scheme");
}
} catch {
throw new Error("Invalid URL");
}
await invoke("open_external", { url });
},
async setAutoLaunch(enabled: boolean): Promise<boolean> {
return await invoke("set_auto_launch", { enabled });
},
async getAutoLaunchStatus(): Promise<boolean> {
return await invoke("get_auto_launch_status");
},
async getToolVersions(
tools?: string[],
wslShellByTool?: Record<View on GitHub (pinned to a2e22f3302)
Solutions
- Restrict UI anchors to absolute http/https URLs before calling openExternal
- If distinct messages matter, hoist the scheme check out of the try block so 'Unsupported URL scheme' survives the catch
- Render custom-protocol links as plain text, or route them through an explicit user-consent dialog
Example fix
// before
try {
const u = new URL(url);
const scheme = u.protocol.replace(":", "").toLowerCase();
if (scheme !== "http" && scheme !== "https") {
throw new Error("Unsupported URL scheme"); // swallowed by catch below
}
} catch {
throw new Error("Invalid URL");
}
// after
let u: URL;
try {
u = new URL(url);
} catch {
throw new Error("Invalid URL");
}
const scheme = u.protocol.replace(":", "").toLowerCase();
if (scheme !== "http" && scheme !== "https") {
throw new Error("Unsupported URL scheme");
} Defensive patterns
Strategy: validation
Validate before calling
function isSafeExternalUrl(url: string): boolean {
try {
const { protocol } = new URL(url);
return protocol === "http:" || protocol === "https:";
} catch {
return false;
}
}
if (isSafeExternalUrl(href)) {
await settingsApi.openExternal(href);
} Type guard
type HttpUrl = `http://${string}` | `https://${string}`;
function isHttpUrl(v: string): v is HttpUrl {
return /^https?:\/\/\S+$/.test(v);
} Try / catch
// The scheme throw is rewritten to 'Invalid URL' by the shared catch,
// so match that message for both malformed and non-http(s) cases.
try {
await settingsApi.openExternal(url);
} catch (e) {
if (e instanceof Error && e.message === "Invalid URL") {
toast("Only http:// and https:// links can be opened");
} else {
throw e;
}
} Prevention
- Whitelist http/https before rendering clickable anchors
- Never pass user-supplied custom-protocol URLs to openExternal
- Keep the scheme check outside the URL-parse try block when distinct error messages matter
When it happens
Trigger: openExternal('file:///etc/hosts'), openExternal('javascript:alert(1)'), openExternal('slack://channel'), openExternal('mailto:a@b.c') - any parseable URL whose protocol is neither http nor https (observed by the caller as 'Invalid URL').
Common situations: Clicking deep links (zoom://, vscode://, msteams://); a user-configurable link field holding a custom protocol; security hardening that forbids file:/javascript: URLs from reaching the OS shell.
Related errors
- Invalid URL
- INVALID_REPO_REF
- pi.form.absoluteHttpUrlRequired
- Invalid skill directory (possible path traversal): {director
- Invalid backup id: {backup_id}
AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16).
Data as JSON: /api/errors/2c7da822852c65e6.
Report an issue: GitHub.