{"record":{"id":"478efaed2e210445","repo":"ToolJet/ToolJet","slug":"quickbookstokenerror","errorCode":null,"errorMessage":"QuickBooksTokenError","messagePattern":"QuickBooksTokenError","errorType":"validation","errorClass":"QueryError","httpStatus":null,"severity":"error","filePath":"marketplace/plugins/quickbooks/lib/index.ts","lineNumber":130,"sourceCode":"        method: 'post',\n        headers: {\n          Authorization: this.getBasicAuthHeader(clientId, clientSecret),\n          'Content-Type': 'application/x-www-form-urlencoded',\n        },\n        body: data.toString(),\n        responseType: 'json',\n      });\n\n      const result = response.body as { access_token?: string; refresh_token?: string };\n\n      if (result.access_token) {\n        return {\n          access_token: result.access_token,\n          refresh_token: result.refresh_token || refreshTokenValue, // preserve existing if not reissued\n        };\n      }\n\n      throw new QueryError('QuickBooksTokenError', 'Access token not found in response', { response: result });\n    } catch (error: any) {\n      if (error instanceof QueryError) throw error;\n      const parsed = error?.response?.body || error;\n      const errorMessage = typeof parsed === 'object' ? (parsed?.error_description || parsed?.error || JSON.stringify(parsed)) : error?.message;\n      console.error('[QuickBooks] Token refresh failed:', errorMessage);\n      throw new QueryError('QuickBooksTokenRefreshError', errorMessage, { status: error?.response?.statusCode, response: parsed });\n    }\n  }\n\n  async run(sourceOptions: any, queryOptions: any, dataSourceId: string): Promise<QueryResult> {\n    const accessToken = sourceOptions['access_token'];\n    const companyId = sourceOptions['company_id'];\n\n    if (!accessToken) {\n      throw new QueryError(\n        'Authentication required',\n        'No access token found. Please connect to QuickBooks first.',\n        { code: 'MISSING_ACCESS_TOKEN' }","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/ToolJet/ToolJet/blob/20602a8e101f2e59686c9afde0d1402aac2c8871/marketplace/plugins/quickbooks/lib/index.ts#L112-L148","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst result = response.body as { access_token?: string; refresh_token?: string };\nif (result.access_token) { return {...}; }\nthrow new QueryError('QuickBooksTokenError', 'Access token not found in response', { response: result });\n\n// after: include raw body + content-type for diagnosis\nif (!result.access_token) {\n  throw new QueryError('QuickBooksTokenError', 'Access token not found in response', {\n    response: result,\n    rawBody: response.rawBody?.toString()?.slice(0, 500),\n    contentType: response.headers['content-type'],\n  });\n}","handlingStrategy":"validation","validationCode":"function looksLikeIntuitTokenResponse(body: any): boolean {\n  return body && typeof body === 'object'\n    && (typeof body.access_token === 'string' || typeof body.refresh_token === 'string');\n}","typeGuard":"function isHtmlInterstitial(body: any, headers: any): boolean {\n  const ct = headers?.['content-type'] || '';\n  return typeof body === 'string' && (ct.includes('text/html') || body.slice(0, 50).toLowerCase().includes('<!doctype'));\n}","tryCatchPattern":"try {\n  return await plugin.refreshToken(sourceOptions, dataSourceId, userId, isAppPublic);\n} catch (e) {\n  if (e instanceof QueryError && e.message === 'QuickBooksTokenError') {\n    // inspect e.data.response; if HTML -> proxy; if schema changed -> patch extraction\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["quickbooks","refresh-token","response-shape","intuit","diagnostics"],"backgroundTag":null,"analyzedSha":"20602a8e101f2e59686c9afde0d1402aac2c8871","analyzedAt":"2026-08-13T05:58:54.221Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}