mongodb/node-mongodb-native · error · MongoCryptAzureKMSRequestError

Malformed response body - missing field `access_token`.

Error message

Malformed response body - missing field `access_token`.

What it means

Thrown when the Azure IMDS returned HTTP 200 and parseable JSON, but the JSON object has no access_token field. The Azure token contract requires access_token; its absence means the response shape is non-conformant. Typically indicates a malformed mock/proxy response or an unexpected IMDS API version mismatch.

Source

Thrown at src/client-side-encryption/providers/azure.ts:84

  body: string;
  status?: number;
}): Promise<AzureTokenCacheEntry> {
  const { status, body: rawBody } = response;

  const body: { expires_in?: number; access_token?: string } = (() => {
    try {
      return JSON.parse(rawBody);
    } catch {
      throw new MongoCryptAzureKMSRequestError('Malformed JSON body in GET request.');
    }
  })();

  if (status !== 200) {
    throw new MongoCryptAzureKMSRequestError('Unable to complete request.', body);
  }

  if (!body.access_token) {
    throw new MongoCryptAzureKMSRequestError(
      'Malformed response body - missing field `access_token`.'
    );
  }

  if (!body.expires_in) {
    throw new MongoCryptAzureKMSRequestError(
      'Malformed response body - missing field `expires_in`.'
    );
  }

  const expiresInMS = Number(body.expires_in) * 1000;
  if (Number.isNaN(expiresInMS)) {
    throw new MongoCryptAzureKMSRequestError(
      'Malformed response body - unable to parse int from `expires_in` field.'
    );
  }

  return {

View on GitHub (pinned to 3366c21a63)

Solutions

  1. If using a custom url in AzureKMSRequestOptions (tests), ensure the mock returns { access_token, expires_in }.
  2. Ensure no intermediary rewrites the IMDS response.
  3. Verify the request includes 'Metadata: true' header and the correct api-version=2018-02-01.
  4. Fall back to explicit azure KMS provider credentials.

Example fix

// before: mock returns { token: '...' }
// after: mock returns { access_token: '...', expires_in: 3600 }
Defensive patterns

Strategy: validation

Validate before calling

// Only relevant if you set AzureKMSRequestOptions.url (tests). Validate your mock shape:
function assertTokenResponse(body: unknown): { access_token: string; expires_in: number } {
  if (typeof body !== 'object' || body === null) throw new Error('not an object');
  const b = body as Record<string, unknown>;
  if (typeof b.access_token !== 'string') throw new Error('missing access_token');
  if (typeof b.expires_in !== 'number') throw new Error('missing expires_in');
  return b as { access_token: string; expires_in: number };
}

Type guard

function isAccessTokenResponse(body: unknown): body is { access_token: string; expires_in: number } {
  return typeof (body as any)?.access_token === 'string' && typeof (body as any)?.expires_in === 'number';
}

Prevention

When it happens

Trigger: In parseResponse() when status === 200 but body.access_token is falsy; seen with custom url/headers test overrides, or when an intermediary returns a 200 JSON body of a different shape.

Common situations: Using AzureKMSRequestOptions.url to point at a mock server whose response omits access_token; api-version query param drift; an API gateway rewriting the response; using a non-Azure metadata service that returns 200 JSON.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/1572906f81eb898d.json. Report an issue: GitHub.