farion1231/cc-switch · warning · Error
Invalid URL
Error message
Invalid URL
What it means
Thrown by settings.openExternal(url) when new URL(url) fails (the value is not a parseable absolute URL) - and, because the scheme-check throw shares the same catch, it is also the message surfaced for any non-http/https scheme. It is the single error callers see for malformed or disallowed input to the external-open API.
Source
Thrown at src/lib/api/settings.ts:223
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<
string,
{ wslShell?: string | null; wslShellFlag?: string | null }
>,View on GitHub (pinned to a2e22f3302)
Solutions
- Normalize input to a full URL first: url.startsWith('http') ? url : `https://${url}`
- Validate with new URL() plus a protocol check in the UI layer before invoking
- Treat an empty string as a no-op click instead of calling the API
Example fix
// before
await settingsApi.openExternal(href); // 'example.com/docs' -> Invalid URL
// after
const normalized = /^https?:\/\//.test(href) ? href : `https://${href}`;
await settingsApi.openExternal(normalized); Defensive patterns
Strategy: validation
Validate before calling
function normalizeExternalUrl(href: string): string | null {
if (!href.trim()) return null;
const candidate = /^https?:\/\//i.test(href) ? href : `https://${href}`;
try {
const { protocol } = new URL(candidate);
return protocol === "http:" || protocol === "https:" ? candidate : null;
} catch {
return null;
}
}
const url = normalizeExternalUrl(href);
if (url) await settingsApi.openExternal(url); Try / catch
try {
await settingsApi.openExternal(url);
} catch (e) {
if (e instanceof Error && e.message === "Invalid URL") {
log.warn("Rejected external URL", { url });
toast("This link is not a valid http(s) URL");
} else {
throw e;
}
} Prevention
- Always store absolute URLs including the scheme
- Default bare domains to https:// at entry time
- Never bind open actions to empty href values
When it happens
Trigger: openExternal('example.com/path') (no scheme, new URL throws), openExternal(''), openExternal('http://'), plus ftp://x and other non-http schemes via the rewritten catch.
Common situations: Config fields storing bare domains without a scheme; template strings producing empty hrefs; the 'Unsupported URL scheme' case masquerading as this message.
Related errors
- Unsupported URL scheme
- pi.form.absoluteHttpUrlRequired
- INVALID_REPO_REF
- OPENCLAW_ENV_EMPTY
- OPENCLAW_ENV_INVALID_JSON
AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16).
Data as JSON: /api/errors/f041c76e9baa8887.
Report an issue: GitHub.