vercel/ai · warning · Error

Invalid resources/read params

Error message

Invalid resources/read params

What it means

The bridge validates `resources/read` requests from the MCP App iframe and requires a `uri` string param. This error is thrown when params are not a JSON object or `params.uri` is missing or not a string. It prevents the host's readResource callback from being called with malformed data.

Source

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

 * Validates the params for app-initiated `tools/call` requests.
 */
function assertToolCallParams(params: unknown): MCPAppToolCallParams {
  if (!isJSONObject(params) || typeof params.name !== 'string') {
    throw new Error('Invalid tools/call params');
  }

  return {
    name: params.name,
    arguments: isJSONObject(params.arguments) ? params.arguments : undefined,
  };
}

/**
 * Validates `resources/read` params and limits reads to `ui://` app resources.
 */
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');
  }

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Ensure the iframe sends `{ method: 'resources/read', params: { uri: '<string>' } }`.
  2. Check for a `uri` vs `url` key typo in the app's request.
  3. Inspect the failing request in the host `onError` callback to see what the iframe actually sent.
  4. Update the app-side MCP SDK to a version that conforms to the MCP Apps resources/read schema.

Example fix

// before
client.readResource({ url: 'ui://resource' })
// after
client.readResource({ uri: 'ui://resource' })
Defensive patterns

Strategy: validation

Validate before calling

function isValidResourceReadParams(params: unknown): boolean {
  return (
    typeof params === 'object' && params !== null && !Array.isArray(params) &&
    typeof (params as any).uri === 'string'
  );
}
// before calling resources/read:
if (!isValidResourceReadParams({ uri })) throw new Error('resources/read requires a string uri');

Type guard

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

Try / catch

try {
  const result = await readResource({ uri });
} catch (error) {
  if (error instanceof Error && error.message === 'Invalid resources/read params') {
    console.error('resources/read params must include a string uri field');
  }
}

Prevention

When it happens

Trigger: The iframe sends `resources/read` with params missing entirely, with `params` not an object, or with a non-string/absent `uri` (e.g. `{ uri: 123 }` or `{}`).

Common situations: A custom app iframe constructed by hand; a spec-noncompliant SDK inside the iframe; passing a resource object instead of its uri string; typo like `url` instead of `uri`.

Related errors


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