different-ai/openwork · error
Only valid web links can be opened externally.
Error message
Only valid web links can be opened externally.
What it means
assertDesktopWebUrl validates a URL before it is handed to the OS shell opener. If `new URL(url)` throws — meaning the string is not a parseable absolute URL — the function refuses to proceed with the generic message 'Only valid web links can be opened externally.' This prevents garbage or relative strings from reaching openExternal/window.open.
Source
Thrown at apps/app/src/app/lib/desktop.ts:475
): Promise<Response> {
if (isLoopbackUrl(input)) {
return globalThis.fetch(input, init);
}
return desktopFetchThroughMain(input, init, {
agentContextDiagnosticsDeadlineAtMs: deadlineAtMs,
});
}
// ---------------------------------------------------------------------------
// Convenience wrappers
// ---------------------------------------------------------------------------
export function assertDesktopWebUrl(url: string): string {
let parsed: URL;
try {
parsed = new URL(url);
} catch {
throw new Error("Only valid web links can be opened externally.");
}
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
throw new Error(`External URL protocol "${parsed.protocol}" is not allowed.`);
}
return parsed.toString();
}
export async function openDesktopUrl(url: string): Promise<void> {
const safeUrl = assertDesktopWebUrl(url);
const openExternal = window.__OPENWORK_ELECTRON__?.shell?.openExternal;
if (openExternal) {
const result = await openExternal(safeUrl);
if (result && result.ok === false) {
throw new Error(result.error ?? "Failed to open browser");
}
return;
}
if (typeof window !== "undefined") {View on GitHub (pinned to 2b7df46e8a)
Solutions
- Ensure the input is an absolute URL including scheme (https://...) before passing it in; prepend a base when the source is relative (`new URL(rel, location.origin).toString()`).
- Trim whitespace and strip stray characters (quotes, trailing punctuation) from user-supplied or copy-pasted links.
- Validate the string with a quick `URL.canParse(url)` (or try/catch around `new URL`) in form validation so the user fixes it before the open attempt.
- Check where the URL originates (config file, env var, API field) and fix the producer to emit full web URLs.
Example fix
// before
await openDesktopUrl(userInput);
// after
const target = userInput.startsWith("http") ? userInput : `https://${userInput}`;
if (!URL.canParse(target)) throw new Error("Please enter a valid link");
await openDesktopUrl(target); Defensive patterns
Strategy: validation
Validate before calling
function isOpenableWebUrl(url: string): boolean {
try { const u = new URL(url); return u.protocol === "http:" || u.protocol === "https:"; } catch { return false; }
} Try / catch
try {
await openDesktopUrl(link);
} catch (err) {
if (err instanceof Error && err.message.includes("Only valid web links")) {
showToast("That is not a valid link");
} else { throw err; }
} Prevention
- Validate URLs at input boundaries (forms, configs) before they reach open helpers.
- Store absolute URLs with schemes in configs/API payloads, never relative paths.
- Use URL.canParse() for cheap pre-checks in modern runtimes.
- Trim and sanitize pasted links before use.
When it happens
Trigger: Calling openDesktopUrl, safeUrl, or waitForManagedMcpAuthorization with a non-URL string: an empty string, a relative path like '/settings', a malformed value like 'http//example.com', or user-pasted text that is not a URL.
Common situations: A config field (e.g. server URL or MCP authorize URL) left blank or containing a typo; reading a link from storage or an API response where it was stored relative instead of absolute; user input from a form passed straight to the opener without trimming/normalizing; locale-mangled copy-paste breaking the scheme.
Related errors
- External URL protocol "${parsed.protocol}" is not allowed.
- Electron desktop helper is unavailable: ${command}
- Failed to open browser
- ${result}
- managed MCP egress requires HTTPS
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/63bc5e15e593e794.
Report an issue: GitHub.