{"record":{"id":"b7b3584a9a60b936","repo":"different-ai/openwork","slug":"failed-to-complete-github-installation-response","errorCode":null,"errorMessage":"Failed to complete GitHub installation (${response.status}).","messagePattern":"Failed to complete GitHub installation \\((.+?)\\)\\.","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/integration-data.tsx","lineNumber":464,"sourceCode":"\n  return useQuery({\n    enabled: Number.isFinite(input.installationId ?? NaN) && (input.installationId ?? 0) > 0 && Boolean(input.state?.trim()),\n    queryKey: [...integrationQueryKeys.githubInstall(input.installationId), input.state ?? \"no-state\"] as const,\n    retry: false,\n    queryFn: async (): Promise<GithubInstallCompleteResult> => {\n      let result: GithubInstallCompleteResult | null = null;\n      await runReauthableAction(\"complete-github-install\", async () => {\n        const { response, payload } = await requestJson(\n          \"/v1/connectors/github/install/complete\",\n          {\n            method: \"POST\",\n            body: JSON.stringify({ installationId: input.installationId, state: input.state }),\n          },\n          20000,\n        );\n\n        if (!response.ok) {\n          throw getRequestError(payload, response, `Failed to complete GitHub installation (${response.status}).`);\n        }\n\n        const item = isRecord(payload) && isRecord(payload.item) ? payload.item : null;\n        const connectorAccount = item && isRecord(item.connectorAccount) ? item.connectorAccount : null;\n        const repositories = item && Array.isArray(item.repositories)\n          ? item.repositories.flatMap((entry) => {\n              if (!isRecord(entry)) {\n                return [];\n              }\n\n              const id = typeof entry.id === \"number\" ? String(entry.id) : asString(entry.id);\n              const fullName = asString(entry.fullName);\n              if (!id || !fullName) {\n                return [];\n              }\n\n              const manifestKindValue = entry.manifestKind;\n              const manifestKind: IntegrationRepoManifestKind = manifestKindValue === \"agent-plugin\" || manifestKindValue === \"marketplace\" || manifestKindValue === \"plugin\"","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/integration-data.tsx#L446-L482","documentation":"Thrown by useGithubInstallCompletion when POST /v1/connectors/github/install/complete (installationId + state) returns non-OK. getRequestError converts 403 reauth payloads to ReauthRequiredError, else throws the server message or this fallback. It means the GitHub App installation callback could not be validated/completed, so the connector account and repo list were not created.","triggerScenarios":"POST /v1/connectors/github/install/complete fails: state token expired or mismatched (400/403 - typically because the tab sat on the GitHub install screen too long or cookies changed), installationId not found/not authorized for the org (404/403), session expired, or 5xx. 20s timeout.","commonSituations":"User leaving the flow open until the one-time state expires; completing an install for a different GitHub account/org than intended; retrying the same completion twice (state consumed); GitHub webhook not reaching the Den server so the installation is unknown.","solutions":["Restart the install flow to mint a fresh state token if the message indicates state expiry/mismatch.","Verify installationId matches the org's configured GitHub App; reinstall under the right account if not.","Re-authenticate on reauth-required errors and re-run the completion.","Check that GitHub App webhooks are delivered to the Den server (installation events required).","Inspect server logs for the completion failure on 5xx."],"exampleFix":"// before\nif (!response.ok) {\n  throw getRequestError(payload, response, `Failed to complete GitHub installation (${response.status}).`);\n}\n// after\nif (!response.ok) {\n  if (response.status === 400 || response.status === 403) {\n    throw new Error(\"GitHub install session expired - restart the installation from the integrations page.\");\n  }\n  throw getRequestError(payload, response, `Failed to complete GitHub installation (${response.status}).`);\n}","handlingStrategy":"retry","validationCode":"// state must be fresh (minted by useStartGithubInstall moments ago)\nif (!input.installationId || !input.state) return; // incomplete callback params from GitHub\nif (completionAttempted) return; // state is single-use; never POST twice","typeGuard":"function hasCompletionInput(v: unknown): v is { installationId: string; state: string } {\n  return typeof v === \"object\" && v !== null\n    && typeof (v as any).installationId === \"string\"\n    && typeof (v as any).state === \"string\";\n}","tryCatchPattern":"try {\n  const result = await completeGithubInstall(input);\n} catch (error) {\n  if (isReauthRequiredError(error)) { promptReauth(); return; }\n  if (error instanceof Error && /state|expired|400|403/i.test(error.message)) {\n    restartInstallFlow(); // mint a fresh state token\n    return;\n  }\n  setConnectError(error instanceof Error ? error.message : \"Could not complete GitHub installation.\");\n}","preventionTips":["Consume the state token exactly once; guard the completion query against refetch/double-run","Restart the install flow (fresh state) whenever completion fails with a state/expiry error","Verify GitHub App webhooks reach the Den server so installations are registered","Complete promptly - do not leave the install screen open until the state expires","Re-authenticate before returning from GitHub if the session may have expired"],"tags":["http","github","integration","network"],"backgroundTag":"github-app-install-failed","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}