ToolJet/ToolJet · error · QueryError
QuickBooksTokenError
Error message
QuickBooksTokenError
What it means
Thrown inside QuickBooks.refreshToken when the POST to TOKEN_URL succeeded (HTTP 200) but the parsed body has no access_token field. Because it is thrown inside the try block, the catch rethrows it verbatim via the `instanceof QueryError` guard. It signals Intuit returned a structurally unexpected success response.
Source
Thrown at marketplace/plugins/quickbooks/lib/index.ts:130
method: 'post',
headers: {
Authorization: this.getBasicAuthHeader(clientId, clientSecret),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: data.toString(),
responseType: 'json',
});
const result = response.body as { access_token?: string; refresh_token?: string };
if (result.access_token) {
return {
access_token: result.access_token,
refresh_token: result.refresh_token || refreshTokenValue, // preserve existing if not reissued
};
}
throw new QueryError('QuickBooksTokenError', 'Access token not found in response', { response: result });
} catch (error: any) {
if (error instanceof QueryError) throw error;
const parsed = error?.response?.body || error;
const errorMessage = typeof parsed === 'object' ? (parsed?.error_description || parsed?.error || JSON.stringify(parsed)) : error?.message;
console.error('[QuickBooks] Token refresh failed:', errorMessage);
throw new QueryError('QuickBooksTokenRefreshError', errorMessage, { status: error?.response?.statusCode, response: parsed });
}
}
async run(sourceOptions: any, queryOptions: any, dataSourceId: string): Promise<QueryResult> {
const accessToken = sourceOptions['access_token'];
const companyId = sourceOptions['company_id'];
if (!accessToken) {
throw new QueryError(
'Authentication required',
'No access token found. Please connect to QuickBooks first.',
{ code: 'MISSING_ACCESS_TOKEN' }View on GitHub (pinned to 20602a8e10)
Solutions
- Log the full result object (data.response) to see what Intuit actually returned — if it is HTML, a proxy is intercepting.
- Verify the got call uses responseType:'json' and that the Content-Type from Intuit is application/json.
- Retry once; transient maintenance pages resolve quickly.
- If the schema genuinely changed, update the access_token extraction path in refreshToken.
Example fix
// before
const result = response.body as { access_token?: string; refresh_token?: string };
if (result.access_token) { return {...}; }
throw new QueryError('QuickBooksTokenError', 'Access token not found in response', { response: result });
// after: include raw body + content-type for diagnosis
if (!result.access_token) {
throw new QueryError('QuickBooksTokenError', 'Access token not found in response', {
response: result,
rawBody: response.rawBody?.toString()?.slice(0, 500),
contentType: response.headers['content-type'],
});
} Defensive patterns
Strategy: validation
Validate before calling
function looksLikeIntuitTokenResponse(body: any): boolean {
return body && typeof body === 'object'
&& (typeof body.access_token === 'string' || typeof body.refresh_token === 'string');
} Type guard
function isHtmlInterstitial(body: any, headers: any): boolean {
const ct = headers?.['content-type'] || '';
return typeof body === 'string' && (ct.includes('text/html') || body.slice(0, 50).toLowerCase().includes('<!doctype'));
} Try / catch
try {
return await plugin.refreshToken(sourceOptions, dataSourceId, userId, isAppPublic);
} catch (e) {
if (e instanceof QueryError && e.message === 'QuickBooksTokenError') {
// inspect e.data.response; if HTML -> proxy; if schema changed -> patch extraction
}
throw e;
} Prevention
- Pin responseType:'json' and assert the response Content-Type is JSON before trusting the body.
- Log rawBody + headers when access_token is missing so proxies are caught fast.
- Monitor Intuit developer notifications for schema changes.
When it happens
Trigger: Intuit changes the token response schema; a proxy returns a 200 with an HTML login/interstitial page that got parses as JSON; responseType mis-parses the body into an object without access_token; the endpoint returned an error-shaped body with a 2xx status.
Common situations: A corporate proxy or captive portal intercepts the HTTPS response with a 200 HTML page. Misconfigured responseType where the body is a string but the code treats it as an object. Intuit maintenance returning a non-standard body.
Related errors
- QuickBooksTokenRefreshError
- access_token not found in the response
- Failed to retrieve access tokens
- MISSING_REFRESH_TOKEN
- Refresh token not found
AI-assisted analysis of ToolJet/ToolJet@20602a8e10 (2026-08-13).
Data as JSON: /api/errors/478efaed2e210445.
Report an issue: GitHub.