{"record":{"id":"db2c1d33674708d3","repo":"paperclipai/paperclip","slug":"github-inventory-failed-this-chat-connection-requ","errorCode":null,"errorMessage":"GitHub inventory failed: this chat connection requires a dedicated GitHub App with exactly one active installation","messagePattern":"GitHub inventory failed: this chat connection requires a dedicated GitHub App with exactly one active installation","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/chat-provider-inventory.ts","lineNumber":303,"sourceCode":"        account?: { id?: number; login?: string; name?: string; type?: string };\n        permissions?: Record<string, string>;\n        suspended_at?: string | null;\n      }>\n    >(response, \"GitHub\");\n    installations.push(...pageInstallations);\n    if (pageInstallations.length < 100) break;\n  }\n  const active = installations.filter(\n    (installation) =>\n      Number.isFinite(installation.id) && !installation.suspended_at,\n  );\n  if (active.length === 0) {\n    throw new Error(\n      \"GitHub inventory failed: install this GitHub App on the selected repositories first\",\n    );\n  }\n  if (active.length !== 1) {\n    throw new Error(\n      \"GitHub inventory failed: this chat connection requires a dedicated GitHub App with exactly one active installation\",\n    );\n  }\n  const installation = active[0]!;\n  const missingPermissions = Object.entries(\n    REQUIRED_GITHUB_INSTALLATION_PERMISSIONS,\n  )\n    .filter(\n      ([permission, access]) =>\n        installation.permissions?.[permission] !== access,\n    )\n    .map(([permission]) => permission);\n  if (missingPermissions.length > 0) {\n    throw new Error(\n      `GitHub inventory failed: the active installation has not granted the required access for: ${missingPermissions.join(\", \")}. Approve the GitHub App permission update, then retry`,\n    );\n  }\n  return {","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-provider-inventory.ts#L285-L321","documentation":"discoverDedicatedGitHubAppInstallation resolves the single active GitHub App installation backing a chat provider connection. This error is thrown when the GitHub App has more than one active installation, because the inventory feature requires a dedicated app installed on exactly one account/repo set so installation id and account can be resolved unambiguously.","triggerScenarios":"Calling the GitHub chat-provider inventory flow (which invokes discoverDedicatedGitHubAppInstallation at server/src/services/chat-provider-inventory.ts:303) while the same GitHub App is installed on two or more accounts or installed both org-wide and per-repo, yielding active.length !== 1.","commonSituations":"A shared/internal GitHub App reused across multiple orgs; an admin installing the app org-wide while a teammate also installed it on a personal account; switching the connection to an app that already serves other repositories.","solutions":["Uninstall the GitHub App from all but the one intended account/repo set, then retry inventory discovery.","Create a dedicated (new) GitHub App used only for this chat connection and install it on exactly the target repositories.","Point the chat connection's GitHub App credentials at an app that has only one installation.","If org-wide plus per-repo installs exist, remove the duplicate and keep a single installation scoped to the selected repositories."],"exampleFix":"// before: shared app installed on org A and org B\n// GitHub App installations: [{account: 'org-a'}, {account: 'org-b'}] -> throws\n// after: uninstall from org-b, leaving exactly one active installation\n// GitHub App installations: [{account: 'org-a'}] -> resolves","handlingStrategy":"validation","validationCode":"const installs = await listAppInstallations(octokit);\nif (installs.filter(i => i.suspended_at === null).length !== 1) {\n  throw new Error(\"Dedicated GitHub App must have exactly one active installation\");\n}","typeGuard":"function hasSingleActiveInstallation(installs: { suspended_at: string | null }[]): boolean {\n  return installs.filter(i => i.suspended_at === null).length === 1;\n}","tryCatchPattern":"try {\n  await discoverDedicatedGitHubAppInstallation(conn);\n} catch (e) {\n  if (e.message.includes(\"exactly one active installation\")) {\n    // surface guidance: uninstall duplicate installations or use a dedicated app\n  }\n}","preventionTips":["Use one dedicated GitHub App per chat connection, never a shared multi-org app.","Before discovery, list installations via the app JWT and assert exactly one active one.","Document that org-wide plus per-repo installs count as multiple installations.","Alert when the app gains a second installation in GitHub settings."],"tags":["github","configuration","inventory"],"backgroundTag":"invalid-config-value","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}