{"record":{"id":"e1994dab90e96cf3","repo":"ToolJet/ToolJet","slug":"could-not-connect-to-googles-calendar","errorCode":null,"errorMessage":"Could not connect to Googles Calendar","messagePattern":"Could not connect to Googles Calendar","errorType":"exception","errorClass":"QueryError","httpStatus":null,"severity":"error","filePath":"marketplace/plugins/googlecalendar/lib/index.ts","lineNumber":313,"sourceCode":"\n      if (result['access_token']) {\n        accessTokenDetails['access_token'] = result['access_token'];\n        accessTokenDetails['refresh_token'] = result['refresh_token'];\n      } else {\n        throw new QueryError(\n          'access_token not found in the response',\n          {},\n          {\n            responseObject: {\n              statusCode: response.statusCode,\n              responseBody: response.body,\n            },\n            responseHeaders: response.headers,\n          }\n        );\n      }\n    } catch (error) {\n      throw new QueryError(\n        'Could not connect to Googles Calendar',\n        JSON.stringify({ statusCode: error.response?.statusCode, message: error.response?.body }),\n        {}\n      );\n    }\n    return accessTokenDetails;\n  }\n}\n","sourceCodeStart":295,"sourceCodeEnd":322,"githubUrl":"https://github.com/ToolJet/ToolJet/blob/20602a8e101f2e59686c9afde0d1402aac2c8871/marketplace/plugins/googlecalendar/lib/index.ts#L295-L322","documentation":"The outer catch in refreshToken() re-wraps any error thrown inside the try as QueryError('Could not connect to Googles Calendar', ...) (note the typo 'Googles'). Because errors 144 and 145 are thrown inside the same try, this catch will re-wrap them — so callers may see this message even when the actual cause was a missing access_token or a 4xx. The description is JSON.stringify of error.response?.statusCode and error.response?.body, but note: QueryError is an Error subclass without a .response property, so when the inner thrown value is itself a QueryError, error.response is undefined and the description becomes '{\"statusCode\":undefined,\"message\":undefined}'.","triggerScenarios":"Any thrown error inside the token-exchange try block: got() network/HTTP error (has .response), or the explicitly-thrown QueryErrors from 144/145 (no .response → unhelpful description). Also fires on JSON.parse failure of the response body.","commonSituations":"Token-exchange endpoint unreachable; Google returned non-JSON; the inner code threw a QueryError which is then mis-wrapped here, hiding the real cause; downstream caller logs only the description string and sees a useless '{statusCode:undefined,message:undefined}'.","solutions":["Re-throw QueryError instances unchanged instead of re-wrapping: `if (err instanceof QueryError) throw err;` at the top of the catch.","When constructing the description, read from the got HTTPError shape (error.response) only when present; otherwise fall back to err.message or err.description.","Fix the typo 'Googles' -> 'Google\\'s' for log searchability.","Inspect err.name/err.constructor.name in the caller to recover the original QueryError if you cannot patch the plugin."],"exampleFix":"// before — blindly re-wraps, destroys inner cause, has typo\n} catch (error) {\n  throw new QueryError(\n    'Could not connect to Googles Calendar',\n    JSON.stringify({ statusCode: error.response?.statusCode, message: error.response?.body }),\n    {}\n  );\n}\n\n// after — preserve inner QueryError, sensible description\n} catch (error) {\n  if (error instanceof QueryError) throw error;\n  throw new QueryError(\n    \"Could not connect to Google's Calendar\",\n    JSON.stringify({ statusCode: error.response?.statusCode, message: error.response?.body }),\n    {}\n  );\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isGoogleCalendarConnectError(e): boolean {\n  return e?.name === 'QueryError'\n      && /Could not connect to Googles? Calendar/.test(e?.message ?? '');\n}","tryCatchPattern":"try {\n  await cal.refreshToken(sourceOptions);\n} catch (e) {\n  // The outer catch destroys the inner cause; recover what we can from description:\n  let parsed = {};\n  try { parsed = JSON.parse(e.description ?? '{}'); } catch {}\n  if (parsed.statusCode === undefined && parsed.message === undefined) {\n    // likely a re-wrapped QueryError (144/145); surface original via patching the plugin\n  }\n  throw e;\n}","preventionTips":["Patch the catch to re-throw QueryError instances unchanged so the inner cause survives.","Fix the 'Googles' typo so log searches match.","Do not rely on this error's description when the inner thrown value lacks .response (it will be undefined)."],"tags":["oauth","google-calendar","error-wrapping","diagnostics","logic-bug","typo","tooljet"],"backgroundTag":null,"analyzedSha":"20602a8e101f2e59686c9afde0d1402aac2c8871","analyzedAt":"2026-08-13T05:58:54.221Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}