mastra-ai/mastra · error

Invalid Copilot models response: missing `data` array

Error message

Invalid Copilot models response: missing `data` array

What it means

After a successful HTTP response, fetchCopilotModels parses the JSON and validates that it has a `data` array per the models API contract. If the body is missing, is not an object, or `data` is not an array, the library throws this contract-validation error rather than iterating over malformed data.

Source

Thrown at mastracode/sdk/src/auth/providers/github-copilot.ts:483

}): Promise<CopilotModelEntry[]> {
  const url = `${opts.baseUrl.replace(/\/$/, '')}/models`;
  const response = await fetch(url, {
    headers: {
      Accept: 'application/json',
      Authorization: `Bearer ${opts.bearerToken}`,
      ...COPILOT_HEADERS,
    },
    signal: opts.signal,
  });

  if (!response.ok) {
    const text = await response.text().catch(() => '');
    throw new Error(`Failed to fetch Copilot models: ${response.status} ${response.statusText}: ${text}`);
  }

  const json = await response.json().catch(() => null);
  if (!json || typeof json !== 'object' || !Array.isArray((json as { data?: unknown }).data)) {
    throw new Error('Invalid Copilot models response: missing `data` array');
  }

  const data = (json as { data: unknown[] }).data;
  const result: CopilotModelEntry[] = [];

  for (const item of data) {
    if (!item || typeof item !== 'object') continue;
    const obj = item as Record<string, unknown>;

    if (obj.model_picker_enabled !== true) continue;

    const policy = obj.policy as Record<string, unknown> | undefined;
    if (policy && policy.state === 'disabled') continue;

    const id = obj.id;
    if (typeof id !== 'string' || !id) continue;

    const name = typeof obj.name === 'string' ? obj.name : id;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Log/inspect the raw response body to see what was actually returned (proxy pages and error JSON are common culprits)
  2. Verify the request targets the official Copilot models endpoint, not a proxy or wrong base URL
  3. Bypass proxies/VPNs and retry to rule out interception
  4. If GitHub changed the API shape, update the SDK/provider version

Example fix

// before (proxy returns 200 + HTML)
baseURL: 'http://proxy.internal/github'
// after
baseURL: 'https://api.individual.githubcopilot.com'
Defensive patterns

Strategy: type-guard

Validate before calling

// Pre-flight probe: fetch and validate the shape yourself before trusting the provider
const res = await fetch(modelsUrl, { headers, signal });
const ct = res.headers.get('content-type') ?? '';
if (!ct.includes('application/json')) {
  throw new Error(`Unexpected content-type from models endpoint: ${ct} (proxy interception?)`);
}

Type guard

function hasDataArray(json: unknown): json is { data: unknown[] } {
  return typeof json === 'object' && json !== null && Array.isArray((json as { data?: unknown }).data);
}

Try / catch

try {
  models = await provider.models();
} catch (err) {
  if (err instanceof Error && err.message.includes('missing `data` array')) {
    // 2xx but wrong shape: log body, check for proxy/captive-portal interference
    console.error('Copilot models endpoint returned a non-contract payload; bypass proxy and retry.');
  } else throw err;
}

Prevention

When it happens

Trigger: The models endpoint returns 2xx with an unexpected body: an HTML login/proxy page, an error JSON without `data` (e.g. {message: ...} from a gateway), or an API version change that renamed/moved the `data` field.

Common situations: Corporate proxy or captive portal returning 200 with HTML; a reverse proxy intercepting the request; GitHub changing the Copilot models API shape; pointing the provider at a non-Copilot endpoint.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/0b58843486ad9bf4. Report an issue: GitHub.