{"record":{"id":"c6faee42225a891d","repo":"vercel/ai","slug":"invalid-ui-open-link-params","errorCode":null,"errorMessage":"Invalid ui/open-link params","messagePattern":"Invalid ui/open-link params","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/react/src/mcp-apps/bridge.ts","lineNumber":87,"sourceCode":"function assertResourceReadParams(params: unknown): { uri: string } {\n  if (!isJSONObject(params) || typeof params.uri !== 'string') {\n    throw new Error('Invalid resources/read params');\n  }\n  if (!params.uri.startsWith('ui://')) {\n    throw new Error(\n      `resources/read is limited to ui:// resources: ${params.uri}`,\n    );\n  }\n  return { uri: params.uri };\n}\n\n/**\n * Validates `ui/open-link` params and allows only `https:`/`http:`/`mailto:`\n * URLs.\n */\nfunction assertOpenLinkParams(params: unknown): { url: string } {\n  if (!isJSONObject(params) || typeof params.url !== 'string') {\n    throw new Error('Invalid ui/open-link params');\n  }\n\n  let scheme: string;\n  try {\n    scheme = new URL(params.url).protocol;\n  } catch {\n    throw new Error(`Invalid ui/open-link url: ${params.url}`);\n  }\n\n  if (scheme !== 'https:' && scheme !== 'http:' && scheme !== 'mailto:') {\n    throw new Error(`Disallowed ui/open-link scheme: ${scheme}`);\n  }\n\n  return { url: params.url };\n}\n\n/**\n * Validates params for `ui/request-display-mode`.","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/react/src/mcp-apps/bridge.ts#L69-L105","documentation":"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.","triggerScenarios":"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://...' }`).","commonSituations":"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.","solutions":["Ensure the iframe sends `{ method: 'ui/open-link', params: { url: '<string>' } }`.","Verify the app actually sets the url value before requesting (log it app-side).","Compare against the MCP Apps `ui/open-link` schema if using a custom iframe client.","Check the host `onError` callback for the raw request payload."],"exampleFix":"// before\nopenLink({ href: 'https://example.com' })\n// after\nopenLink({ url: 'https://example.com' })","handlingStrategy":"validation","validationCode":"function isValidOpenLinkParams(params: unknown): boolean {\n  return (\n    typeof params === 'object' && params !== null && !Array.isArray(params) &&\n    typeof (params as any).url === 'string'\n  );\n}\n// before requesting:\nif (!isValidOpenLinkParams({ url })) throw new Error('ui/open-link requires a string url');","typeGuard":"function isOpenLinkParams(v: unknown): v is { url: string } {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) &&\n    typeof (v as any).url === 'string';\n}","tryCatchPattern":"try {\n  await openLink({ url });\n} catch (error) {\n  if (error instanceof Error && error.message === 'Invalid ui/open-link params') {\n    console.error('ui/open-link params must be { url: string }');\n  }\n}","preventionTips":["Use the exact `url` key with a string value.","Guard against undefined link variables before building the request.","Validate the params object app-side before posting."],"tags":["mcp-apps","validation","iframe","json-rpc"],"backgroundTag":"invalid-rpc-params","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}