{"record":{"id":"9430c6fa697cfd46","repo":"ComposioHQ/composio","slug":"failed-to-fetch-toolkits","errorCode":null,"errorMessage":"Failed to fetch toolkits","messagePattern":"Failed to fetch toolkits","errorType":"exception","errorClass":"ComposioToolkitFetchError","httpStatus":null,"severity":"error","filePath":"ts/packages/core/src/models/Toolkits.ts","lineNumber":81,"sourceCode":"      }\n      const listParams = {\n        category: parsedQuery.data.category,\n        managed_by: parsedQuery.data.managedBy,\n        sort_by: parsedQuery.data.sortBy,\n        cursor: parsedQuery.data.cursor,\n        limit: parsedQuery.data.limit,\n      };\n      const result = await withCancellation(\n        () => this.client.toolkits.list(listParams, requestOptions),\n        requestOptions?.signal\n      );\n\n      return transformToolkitListResponse(result);\n    } catch (error) {\n      if (error instanceof ComposioRequestCancelledError) {\n        throw error;\n      }\n      throw new ComposioToolkitFetchError('Failed to fetch toolkits', {\n        cause: error,\n      });\n    }\n  }\n  /**\n   * Retrieves a specific toolkit by its slug identifier.\n   *\n   * This method fetches a single toolkit from the Composio API and transforms\n   * the response to use camelCase property naming consistent with JavaScript/TypeScript conventions.\n   *\n   * @param {string} slug - The unique slug identifier of the toolkit to retrieve\n   * @returns {Promise<ToolkitRetrieveResponse>} The transformed toolkit object\n   * @throws {ValidationError} If the response cannot be properly parsed\n   * @throws {ComposioToolNotFoundError} If no toolkit with the given slug exists\n   *\n   * @private\n   */\n  protected async getToolkitBySlug(","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/ts/packages/core/src/models/Toolkits.ts#L63-L99","documentation":"getToolkits() wraps its entire body in a try/catch and converts any non-cancellation failure (network error, APIError, or unexpected exception) into ComposioToolkitFetchError with the original as cause. It rethrows ComposioRequestCancelledError untouched so aborted requests are not misclassified. Hitting it means the toolkit list endpoint could not be fetched to completion.","triggerScenarios":"composio.toolkits.get() when the network is down, the API key is rejected (401), the API returns 5xx, or a request timeout/abort that is not a explicit cancellation.","commonSituations":"Invalid or expired API key, wrong base URL / environment, regional outage, or flaky connectivity during listing.","solutions":["Verify the API key and base URL configuration are correct","Inspect error.cause: APIError.status reveals 401/403 vs 5xx vs TypeError (network)","Retry with backoff for transient 5xx/network failures","Check the Composio status page for ongoing incidents"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"const isToolkitFetchError = (e: unknown): e is ComposioToolkitFetchError => e instanceof ComposioToolkitFetchError;","tryCatchPattern":"try { const list = await composio.toolkits.get(query); } catch (e) {\n  if (e instanceof ComposioRequestCancelledError) throw e;\n  if (e instanceof ComposioToolkitFetchError) { const status = (e.cause as any)?.status; if (status >= 500 || !status) { /* retry with backoff */ } else throw e; }\n}","preventionTips":["Validate API key and base URL at app startup","Wrap listing calls in retry with exponential backoff for 5xx/network causes","Distinguish e.cause.status codes before retrying blindly"],"tags":["network","api-error","toolkits","fetch"],"backgroundTag":"api-request-failed","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}