aaif-goose/goose · error

Resource '${fallbackUri}' returned no contents

Error message

Resource '${fallbackUri}' returned no contents

What it means

Thrown by flattenReadResourceResult in mcp-apps.ts when an MCP readResource result has no usable contents: either result.contents is missing/not an array, or no element of it passes isRecord. The function expects the standard MCP ReadResourceResult shape ({contents: [{uri, mimeType?, text? | blob?}]}) and takes the first object entry. An empty array means the server acknowledged the read but returned nothing for the requested URI.

Source

Thrown at ui/desktop/src/acp/mcp-apps.ts:51

  return isRecord(meta) ? meta : undefined;
}

function decodeBase64Text(blob: string): string {
  let bytes: Uint8Array;
  if (typeof globalThis.atob === 'function') {
    const binary = globalThis.atob(blob);
    bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
  } else {
    bytes = Uint8Array.from(Buffer.from(blob, 'base64'));
  }
  return new TextDecoder().decode(bytes);
}

function flattenReadResourceResult(result: unknown, fallbackUri: string): McpAppResourceResponse {
  const contents = isRecord(result) && Array.isArray(result.contents) ? result.contents : [];
  const first = contents.find(isRecord);
  if (!first) {
    throw new Error(`Resource '${fallbackUri}' returned no contents`);
  }

  const uri = stringField(first, 'uri') ?? fallbackUri;
  const mimeType = stringField(first, 'mimeType') ?? stringField(first, 'mime_type') ?? null;
  const text = stringField(first, 'text') ?? decodeBase64Text(stringField(first, 'blob') ?? '');

  return {
    uri,
    mimeType,
    text,
    _meta: metaField(first),
  };
}

function acpApp(value: unknown): GooseApp | null {
  if (!isRecord(value)) return null;
  return value as GooseApp;
}

View on GitHub (pinned to 3810898a74)

Solutions

  1. Call the server's resources/list and confirm the requested URI exists before reading it.
  2. Inspect the raw readResource response in devtools/network to see the actual shape — if it is nested, adjust the flattening.
  3. Fix the MCP server to return an explicit error (or valid contents) instead of an empty contents array.
  4. If the resource is legitimately empty, handle this error in the caller and show 'resource unavailable' rather than crashing the app flow.

Example fix

// before
const contents = isRecord(result) && Array.isArray(result.contents) ? result.contents : [];
const first = contents.find(isRecord);
if (!first) {
  throw new Error(`Resource '${fallbackUri}' returned no contents`);
}

// after (clearer error including what came back)
const contents = isRecord(result) && Array.isArray(result.contents) ? result.contents : [];
const first = contents.find(isRecord);
if (!first) {
  throw new Error(`Resource '${fallbackUri}' returned no contents (got ${JSON.stringify(result)?.slice(0, 200)})`);
}
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the resource exists before reading it
const listed = await client.readResource ?? null; // placeholder guard for typed clients
async function resourceExists(client: McpClient, uri: string): Promise<boolean> {
  const resources = await client.listResources();
  return resources.some((r) => r.uri === uri);
}

Type guard

function hasResourceContents(
  result: unknown
): result is { contents: Record<string, unknown>[] } {
  return (
    typeof result === 'object' && result !== null &&
    Array.isArray((result as { contents?: unknown }).contents) &&
    ((result as { contents: unknown[] }).contents.length > 0)
  );
}

Try / catch

try {
  const response = await readMcpAppResource(uri);
} catch (error) {
  if (/returned no contents/.test(String(error))) {
    return { uri, mimeType: null, text: '', _meta: {} }; // or show 'resource unavailable'
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling the MCP app resource fetch (readResource with a fallbackUri) where the app/server does not expose that resource; server bug returning {contents: []}; a response shaped differently (e.g. {result: {contents: [...]}}) so the outer contents check fails; empty resource file behind the server.

Common situations: Deep links or app-install flows requesting a manifest/resource URI the server version no longer serves; MCP server returning 200 with empty contents for unknown URIs instead of an error; schema drift between the server and this client's expectations.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/8e4e51bce34f4c34. Report an issue: GitHub.