{"record":{"id":"4544b526f0e561bf","repo":"tinyhumansai/openhuman","slug":"failed-to-list-branches","errorCode":null,"errorMessage":"Failed to list branches","messagePattern":"Failed to list branches","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/components/skills/inputs/BranchPicker.tsx","lineNumber":72,"sourceCode":"      setError(null);\n      return;\n    }\n    const [owner, repoName] = repo.split('/');\n    if (!owner || !repoName) {\n      setBranches([]);\n      return;\n    }\n    const seq = ++requestSeqRef.current;\n    setLoading(true);\n    setError(null);\n    try {\n      const res = await composioExecute('GITHUB_LIST_BRANCHES', {\n        owner,\n        repo: repoName,\n        per_page: 100,\n      });\n      if (seq !== requestSeqRef.current) return;\n      if (!res.successful) throw new Error(res.error ?? 'Failed to list branches');\n      // Composio wraps GitHub branch data in a few different shapes\n      // (details, data.details, branches, items, direct array under data) —\n      // probe the same way DevWorkflowPanel does.\n      const raw = res.data;\n      let list: GhBranch[] = [];\n      if (Array.isArray(raw)) {\n        list = raw as GhBranch[];\n      } else if (raw && typeof raw === 'object') {\n        const obj = raw as Record<string, unknown>;\n        const dataObj = obj.data as Record<string, unknown> | undefined;\n        const arr =\n          (obj.details as unknown[] | undefined) ??\n          (dataObj?.details as unknown[] | undefined) ??\n          (obj.branches as unknown[] | undefined) ??\n          (obj.items as unknown[] | undefined) ??\n          (dataObj as unknown[] | undefined);\n        if (Array.isArray(arr)) {\n          list = arr as GhBranch[];","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/components/skills/inputs/BranchPicker.tsx#L54-L90","documentation":"BranchPicker calls Composio's GITHUB_LIST_BRANCHES for the chosen owner/repo (per_page 100); a successful=false response with an empty error string falls back to this message. The GitHub connection is not re-verified here — the repo was already selected upstream.","triggerScenarios":"Composio action fails with res.error empty: token lost access to the repo, repo renamed/deleted/made private, GitHub rate limit.","commonSituations":"Repo transferred or privated after the connection was made; GitHub App scopes narrowed; bursts of branch listing hitting rate limits.","solutions":["Confirm the repo still exists and the connected account can see it, then retry","Reconnect GitHub via Composio if the token lost repo scope","Re-pick the repository (or retry later) if listing keeps failing"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Guard the inputs before the Composio call:\nif (!owner.trim() || !repoName.trim() || /\\s/.test(`${owner}${repoName}`)) {\n  // block fetch: malformed owner/repo always fails the action\n}","typeGuard":"const isBranchListResult = (raw: unknown): boolean =>\n  Array.isArray(raw) || !!((raw as Record<string, unknown>)?.details);","tryCatchPattern":"try {\n  const res = await composioExecute('GITHUB_LIST_BRANCHES', { owner, repo, per_page: 100 });\n  if (!res.successful) { setError(res.error ?? 'Failed to list branches'); return; }\n} catch (e) {\n  // network/Composio-level failure: offer retry and reconnect-GitHub hint\n}","preventionTips":["Confirm the repo is still visible to the connected account before drilling into branches","Reconnect GitHub via Composio when listings suddenly start failing after working before","Respect per_page limits and debounce rapid repo switching (requestSeq pattern is already in place)"],"tags":["composio","github","branches","workflow-input"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}