ToolJet/ToolJet · error · QueryError

Refresh token failed

Error message

Refresh token failed

What it means

Thrown in refreshToken() at marketplace/plugins/microsoft_graph/lib/index.ts:419 when the token endpoint POST succeeded (got did not throw) but the parsed response body has no access_token. QueryError('Refresh token failed', 'Access token not found in response', {}). It indicates a 2xx response with an unexpected shape rather than an HTTP error. Note: because this throw sits inside the same try, it is normally re-caught at index.ts:421 and rewrapped as 'Error while generating refresh access token' (error 176).

Source

Thrown at marketplace/plugins/microsoft_graph/lib/index.ts:419

    };

    const headers = {
      'Content-Type': 'application/x-www-form-urlencoded',
    };

    try {
      const response = await got(tokenEndpoint, {
        method: 'post',
        headers,
        form: tokenRequestBody,
        responseType: 'json', // Automatically parse JSON response
      });
      const result = response.body;
      if (result['access_token']) {
        accessTokenDetails['access_token'] = result['access_token'];
        accessTokenDetails['refresh_token'] = result['refresh_token'];
      } else {
        throw new QueryError('Refresh token failed', 'Access token not found in response', {});
      }
    } catch (error) {
      throw new QueryError('Error while generating refresh access token', JSON.stringify(error), {});
    }
    return accessTokenDetails;
  }
}

View on GitHub (pinned to 20602a8e10)

Solutions

  1. Log the full response body (the third arg of the resulting QueryError, once re-caught) to see what was returned instead of a token.
  2. Verify the tokenEndpoint URL is correct for the tenant (https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token).
  3. If a proxy is intercepting, exclude Microsoft login endpoints or configure got to bypass it.
  4. Re-authenticate to obtain a fresh refresh_token and retry the exchange.

Example fix

// before: no visibility into a 200-without-token response
if (result['access_token']) { ... } else { throw new QueryError('Refresh token failed', 'Access token not found in response', {}); }

// after: include the unexpected body in the error for diagnostics
if (result['access_token']) { ... } else { throw new QueryError('Refresh token failed', 'Access token not found in response', { body: result }); }
Defensive patterns

Strategy: try-catch

Type guard

function hasAccessToken(body: unknown): body is { access_token: string; refresh_token?: string } {
  return typeof (body as any)?.access_token === 'string' && (body as any).access_token.length > 0;
}

Try / catch

import { QueryError } from '@tooljet-marketplace/common';

try {
  await plugin.refreshToken(sourceOptions, dataSourceId, userId, isAppPublic);
} catch (e) {
  // error 175 is normally re-caught as 176; detect by description text
  if (e instanceof QueryError && /Access token not found in response/i.test(String(e.description))) {
    // inspect the response body captured in details, then re-authenticate
  } else throw e;
}

Prevention

When it happens

Trigger: Microsoft returns 200 with an error object instead of a token (rare, conditional access or malformed grant handled gracefully by the endpoint); responseType:'json' parsed the body into an object lacking access_token; tenant endpoint returned an unexpected schema; intermediary (proxy) returning a 200 status page.

Common situations: A transparent proxy returning its own 200 HTML/JSON; Azure conditional access returning a challenge body; partial response due to network truncation parsed as valid JSON; tenant misconfiguration routing to a non-token endpoint.

Related errors


AI-assisted analysis of ToolJet/ToolJet@20602a8e10 (2026-08-13). Data as JSON: /api/errors/12ce9eda37b6574c. Report an issue: GitHub.