mastra-ai/mastra · error

Invalid device code response fields

Error message

Invalid device code response fields

What it means

After the device-code response parsed as an object, `startDeviceFlow` checks that `device_code`, `user_code`, `verification_uri` are strings and `interval`, `expires_in` are numbers. A missing or mistyped field means GitHub answered ok but the payload is not a valid device-code response, so the SDK refuses to continue the polling flow. This is a defensive schema validation against contract drift or non-GitHub responses.

Solutions

  1. Inspect the actual response object (log it before this call) to see which field is missing or mistyped
  2. Confirm the domain points at a real GitHub/GHES device-flow endpoint, not a proxy or mock
  3. Check for an error object in the response (`error`, `error_description`) and surface that message instead
  4. Upgrade the SDK — field validation may need updating after a GitHub API change
  5. File/verify against GHES release notes if using GitHub Enterprise Server, as its device-flow payload can lag github.com

Example fix

// before: swallowing the real payload
const d = await provider.device(); // throws 'Invalid device code response fields'
// after: capture and inspect the raw response to see the real error
const raw = await fetch(deviceCodeUrl, { method: 'POST', ... });
const body = await raw.json();
if (body.error) throw new Error(`GitHub: ${body.error} - ${body.error_description}`);
console.log(body); // then compare with expected device_code/user_code fields
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate the raw device-flow payload shape yourself before/alongside the SDK call
function looksLikeDeviceCode(o: unknown): boolean {
  return !!o && typeof o === 'object' && ['device_code','user_code','verification_uri','interval','expires_in'].every(k => k in (o as Record<string, unknown>));
}

Type guard

function isValidDeviceCodeFields(v: unknown): v is { device_code: string; user_code: string; verification_uri: string; interval: number; expires_in: number } {
  if (!v || typeof v !== 'object') 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 fields') {
    // Surface the raw payload to see the actual error envelope or field drift
    const raw = await fetchRawDeviceCode(); // your own diagnostic call
    if (raw && typeof raw === 'object' && 'error' in (raw as object)) {
      throw new Error(`GitHub device flow: ${(raw as any).error} - ${(raw as any).error_description}`);
    }
    throw new Error(`Device-code fields missing/mistyped: ${JSON.stringify(raw)}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: GitHub returns an object without the standard device-flow fields — e.g. an error object like `{error:"...", error_description:"..."}` delivered with a 200, a GHES instance with a divergent device-flow implementation, or an API version change renaming/omitting fields.

Common situations: GHES or GitHub proxy endpoints with older/newer device-flow implementations, interception by a mock or gateway returning partial JSON, GitHub deprecating/changing field types in a future API version.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

  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');
  }

  return {
    device_code: deviceCode,
    user_code: userCode,
    verification_uri: verificationUri,
    interval,
    expires_in: expiresIn,
  };
}

/** Sleep that can be interrupted by an AbortSignal. */
function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {
  return new Promise((resolve, reject) => {
    if (signal?.aborted) {
      reject(new Error('Login cancelled'));
      return;
    }

View on GitHub (pinned to 75dd419e61)