mastra-ai/mastra · error

Invalid device code response

Error message

Invalid device code response

What it means

`startDeviceFlow` validates that the GitHub device-code endpoint returned a JSON object before reading fields. If the response parsed as JSON but was not an object (or was empty), the SDK throws this error. It indicates the endpoint responded successfully (HTTP ok) but with an unexpected payload, usually because the URL does not actually serve the GitHub device-flow API.

Source

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

  const data = await fetchJson(
    urls.deviceCodeUrl,
    {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': COPILOT_USER_AGENT,
      },
      body: new URLSearchParams({
        client_id: CLIENT_ID,
        scope: 'read:user',
      }),
    },
    signal,
  );

  if (!data || typeof data !== 'object') {
    throw new Error('Invalid device code response');
  }

  const obj = data as Record<string, unknown>;
  const deviceCode = obj.device_code;
  const userCode = obj.user_code;
  const verificationUri = obj.verification_uri;
  const interval = obj.interval;
  const expiresIn = obj.expires_in;

  if (
    typeof deviceCode !== 'string' ||
    typeof userCode !== 'string' ||
    typeof verificationUri !== 'string' ||
    typeof interval !== 'number' ||
    typeof expiresIn !== 'number'
  ) {
    throw new Error('Invalid device code response fields');
  }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Verify the GitHub domain config — the resolved `deviceCodeUrl` must be `https://<github.com or GHES host>/login/device/code`
  2. Log/print the raw response body of the device-code request to see what was actually returned
  3. Bypass or correctly configure proxies/SSL inspection that rewrite GitHub responses
  4. Test the endpoint directly with curl to confirm it returns a JSON object with device_code
  5. If on GHES, confirm Copilot/OAuth device flow is enabled on that instance

Example fix

// before: misconfigured domain leads to unexpected response
const provider = createGitHubCopilotProvider({ domain: 'github.mycompany.com' });
// after: verify the correct GitHub host (GHES base host, not api. subdomain)
const provider = createGitHubCopilotProvider({ domain: 'github.example.com' }); // if that is the real GHES host
curl -i https://github.example.com/login/device/code -d 'client_id=...&scope=read:user' // confirm JSON object
Defensive patterns

Strategy: type-guard

Validate before calling

// Sanity-check the endpoint returns a JSON object before the SDK consumes it
const probe = await fetch(deviceCodeUrl, { method: 'POST', headers: { Accept: 'application/json' }, body: new URLSearchParams({ client_id: 'Iv1.b507a08c87ecfe98', scope: 'read:user' }) });
const probeBody = await probe.json().catch(() => null);
if (!probeBody || typeof probeBody !== 'object' || Array.isArray(probeBody)) throw new Error(`Unexpected device-code payload from ${deviceCodeUrl}`);

Type guard

function isDeviceCodeResponse(v: unknown): v is { device_code: string; user_code: string; verification_uri: string; interval: number; expires_in: number } {
  if (!v || typeof v !== 'object' || Array.isArray(v)) return false;
  const o = v as Record<string, unknown>;
  return typeof o.device_code === 'string' && typeof o.user_code === 'string' && typeof o.verification_uri === 'string' && typeof o.interval === 'number' && typeof o.expires_in === 'number';
}

Try / catch

try {
  const pending = await provider.device();
} catch (e) {
  if (e instanceof Error && e.message === 'Invalid device code response') {
    console.error('Device-code endpoint did not return a JSON object. Check the configured GitHub domain/proxy:', e.message);
    return reconfigureDomainOrProxy();
  }
  throw e;
}

Prevention

When it happens

Trigger: The device-code URL returned 200 with an empty body, an array, a string, or an HTML/login page that fetch's `response.json()` accepted (rare) — e.g. a GHES domain whose `getUrls` produced a wrong `deviceCodeUrl`, or an intermediate proxy/gateway returning an ok response with a non-JSON-object body.

Common situations: Typo'd or outdated enterprise domain in config pointing at a page/proxy instead of the OAuth endpoints, a corporate proxy intercepting github.com, or a GitHub API change/version where the endpoint shape differs.

Related errors


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