vercel/ai · warning · Error
Invalid ui/open-link url: ${params.url}
Error message
Invalid ui/open-link url: ${params.url} What it means
The `url` string supplied by the iframe for `ui/open-link` could not be parsed by the `URL` constructor, meaning it is not an absolute, well-formed URL. The bridge rejects it before invoking the host's openLink handler, since relative or garbage strings cannot be safely opened.
Source
Thrown at packages/react/src/mcp-apps/bridge.ts:94
);
}
return { uri: params.uri };
}
/**
* Validates `ui/open-link` params and allows only `https:`/`http:`/`mailto:`
* URLs.
*/
function assertOpenLinkParams(params: unknown): { url: string } {
if (!isJSONObject(params) || typeof params.url !== 'string') {
throw new Error('Invalid ui/open-link params');
}
let scheme: string;
try {
scheme = new URL(params.url).protocol;
} catch {
throw new Error(`Invalid ui/open-link url: ${params.url}`);
}
if (scheme !== 'https:' && scheme !== 'http:' && scheme !== 'mailto:') {
throw new Error(`Disallowed ui/open-link scheme: ${scheme}`);
}
return { url: params.url };
}
/**
* Validates params for `ui/request-display-mode`.
*/
function assertDisplayModeParams(params: unknown): {
mode: 'inline' | 'fullscreen' | 'pip';
} {
if (
!isJSONObject(params) ||
(params.mode !== 'inline' &&View on GitHub (pinned to 69428b1f8b)
Solutions
- Make the iframe send an absolute URL including scheme, e.g. `https://example.com/page`.
- Resolve relative paths against `window.location.origin` before calling ui/open-link.
- Trim/encode the url string and validate it with `new URL(url)` app-side first.
- If links come from a backend, fix the backend to emit absolute URLs.
Example fix
// before
openLink({ url: 'example.com/docs' })
// after
openLink({ url: 'https://example.com/docs' }) Defensive patterns
Strategy: validation
Validate before calling
// app-side, before requesting:
let parsed: URL;
try {
parsed = new URL(url); // throws on relative/malformed urls
} catch {
parsed = new URL(url, window.location.origin); // resolve relative paths
} Type guard
function isAbsoluteUrl(value: string): boolean {
try { new URL(value); return true; } catch { return false; }
} Try / catch
try {
await openLink({ url });
} catch (error) {
if (error instanceof Error && error.message.startsWith('Invalid ui/open-link url')) {
// retry with an absolutized, trimmed url
await openLink({ url: new URL(url.trim(), window.location.origin).href });
}
} Prevention
- Always send absolute URLs with an explicit scheme.
- Resolve relative paths against location.origin app-side.
- Trim whitespace and encode the url before sending.
- Sanitize user-supplied link text before using it as a url.
When it happens
Trigger: The iframe sends `ui/open-link` with a url like `"example.com/page"` (no scheme), `"/relative/path"`, an empty string, or a string containing spaces/invalid characters that fail `new URL()`.
Common situations: App constructs links from template strings with missing scheme; user-supplied text passed straight through as the url; backend returning relative paths that were never resolved to absolute URLs.
Related errors
- Invalid tools/call params
- Invalid resources/read params
- Invalid ui/open-link params
- Disallowed ui/open-link scheme: ${scheme}
- Invalid ui/request-display-mode params
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/f953be0a39906e23.
Report an issue: GitHub.