vercel/ai · warning · Error

Invalid ui/open-link params

Error message

Invalid ui/open-link params

What it means

The bridge validates `ui/open-link` requests and requires a string `url` param. This error is thrown when params are not a JSON object or `params.url` is missing/not a string. It guards the host's openLink callback from malformed iframe input.

Source

Thrown at packages/react/src/mcp-apps/bridge.ts:87

function assertResourceReadParams(params: unknown): { uri: string } {
  if (!isJSONObject(params) || typeof params.uri !== 'string') {
    throw new Error('Invalid resources/read params');
  }
  if (!params.uri.startsWith('ui://')) {
    throw new Error(
      `resources/read is limited to ui:// resources: ${params.uri}`,
    );
  }
  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`.

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Ensure the iframe sends `{ method: 'ui/open-link', params: { url: '<string>' } }`.
  2. Verify the app actually sets the url value before requesting (log it app-side).
  3. Compare against the MCP Apps `ui/open-link` schema if using a custom iframe client.
  4. Check the host `onError` callback for the raw request payload.

Example fix

// before
openLink({ href: 'https://example.com' })
// after
openLink({ url: 'https://example.com' })
Defensive patterns

Strategy: validation

Validate before calling

function isValidOpenLinkParams(params: unknown): boolean {
  return (
    typeof params === 'object' && params !== null && !Array.isArray(params) &&
    typeof (params as any).url === 'string'
  );
}
// before requesting:
if (!isValidOpenLinkParams({ url })) throw new Error('ui/open-link requires a string url');

Type guard

function isOpenLinkParams(v: unknown): v is { url: string } {
  return typeof v === 'object' && v !== null && !Array.isArray(v) &&
    typeof (v as any).url === 'string';
}

Try / catch

try {
  await openLink({ url });
} catch (error) {
  if (error instanceof Error && error.message === 'Invalid ui/open-link params') {
    console.error('ui/open-link params must be { url: string }');
  }
}

Prevention

When it happens

Trigger: The iframe posts `ui/open-link` with `params` not an object, no `url` key, or a non-string `url` value (e.g. `{ url: undefined }` or `{ href: 'https://...' }`).

Common situations: App UI builds the link dynamically and the variable is undefined; key typo (`href`/`uri` instead of `url`); a hand-written iframe client deviating from the MCP Apps method schema.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/c6faee42225a891d. Report an issue: GitHub.