{"record":{"id":"86aef9e79a1ecc20","repo":"tinyhumansai/openhuman","slug":"failed-to-fetch-repositories-86aef9","errorCode":null,"errorMessage":"Failed to fetch repositories","messagePattern":"Failed to fetch repositories","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/components/skills/inputs/RepoPicker.tsx","lineNumber":69,"sourceCode":"  const [error, setError] = useState<string | null>(null);\n\n  // ── Fetch repos via Composio (mirrors DevWorkflowPanel) ────────────\n  const loadRepos = useCallback(async () => {\n    setLoading(true);\n    setError(null);\n    try {\n      // Step 1: Is GitHub connected via Composio?\n      const conns = await listConnections();\n      const ghConn = conns.connections?.find(\n        c =>\n          c.toolkit.toLowerCase().includes('github') &&\n          (c.status === 'ACTIVE' || c.status === 'CONNECTED')\n      );\n      if (!ghConn) throw new Error('NOT_CONNECTED');\n\n      // Step 2: Fetch repos.\n      const res = await composioExecute('GITHUB_LIST_REPOSITORIES_FOR_THE_AUTHENTICATED_USER', {});\n      if (!res.successful) throw new Error(res.error ?? 'Failed to fetch repositories');\n\n      // Step 3: Parse — GitHub API returns an array of repo objects;\n      // Composio sometimes wraps it under `.repositories`.\n      const raw = res.data;\n      const items = Array.isArray(raw)\n        ? raw\n        : ((raw as Record<string, unknown>)?.repositories ?? []);\n      const list: ComposioGhRepo[] = Array.isArray(items)\n        ? (items as Record<string, unknown>[]).map(r => ({\n            owner: String((r.owner as Record<string, unknown>)?.login ?? r.owner ?? ''),\n            repo: String(r.name ?? ''),\n            fullName: String(\n              r.full_name ?? `${(r.owner as Record<string, unknown>)?.login ?? r.owner}/${r.name}`\n            ),\n            private: r.private as boolean | undefined,\n            defaultBranch: r.default_branch as string | undefined,\n            htmlUrl: r.html_url as string | undefined,\n          }))","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/components/skills/inputs/RepoPicker.tsx#L51-L87","documentation":"RepoPicker verifies an active GitHub connection first (throwing 'NOT_CONNECTED' otherwise), then runs GITHUB_LIST_REPOSITORIES_FOR_THE_AUTHENTICATED_USER via Composio; when the action returns successful=false with an empty error string this fallback message is thrown.","triggerScenarios":"Composio executes the action but reports failure with res.error empty: rate limit, stale Composio connection token, or a GitHub API error swallowed upstream.","commonSituations":"Composio connection needs re-auth after token expiry; Composio throttling or outage; OAuth grant revoked on GitHub.","solutions":["Verify the GitHub (Composio) connection is ACTIVE in Connections; reconnect if stale","Retry after a short wait for rate limits to clear","Reproduce the action from the Composio dashboard to see the underlying error; check Composio status"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Gate the picker on connection freshness before any fetch:\nconst conns = await listConnections();\nconst gh = conns.connections?.find(c =>\n  c.toolkit.toLowerCase().includes('github') &&\n  (c.status === 'ACTIVE' || c.status === 'CONNECTED')\n);\nif (!gh) { /* show connect prompt, skip the Composio call entirely */ }","typeGuard":"const isActiveGithub = (c: { toolkit: string; status: string }): boolean =>\n  c.toolkit.toLowerCase().includes('github') &&\n  (c.status === 'ACTIVE' || c.status === 'CONNECTED');","tryCatchPattern":"try {\n  await loadRepos();\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  if (msg === 'NOT_CONNECTED') { promptConnectGithub(); return; }\n  if (msg === 'Failed to fetch repositories') { offerRetryAfterDelay(); return; }\n  throw e;\n}","preventionTips":["Keep the Composio GitHub connection ACTIVE; re-auth on expiry","Handle 'NOT_CONNECTED' distinctly from the empty-error fallback","Retry once after a short delay — rate limits and blips dominate this path"],"tags":["composio","github","integration","repositories"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}