different-ai/openwork · error · Error
Failed to complete GitHub installation (${response.status}).
Error message
Failed to complete GitHub installation (${response.status}). What it means
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.
Source
Thrown at ee/apps/den-web/app/(den)/dashboard/_components/integration-data.tsx:464
return useQuery({
enabled: Number.isFinite(input.installationId ?? NaN) && (input.installationId ?? 0) > 0 && Boolean(input.state?.trim()),
queryKey: [...integrationQueryKeys.githubInstall(input.installationId), input.state ?? "no-state"] as const,
retry: false,
queryFn: async (): Promise<GithubInstallCompleteResult> => {
let result: GithubInstallCompleteResult | null = null;
await runReauthableAction("complete-github-install", async () => {
const { response, payload } = await requestJson(
"/v1/connectors/github/install/complete",
{
method: "POST",
body: JSON.stringify({ installationId: input.installationId, state: input.state }),
},
20000,
);
if (!response.ok) {
throw getRequestError(payload, response, `Failed to complete GitHub installation (${response.status}).`);
}
const item = isRecord(payload) && isRecord(payload.item) ? payload.item : null;
const connectorAccount = item && isRecord(item.connectorAccount) ? item.connectorAccount : null;
const repositories = item && Array.isArray(item.repositories)
? item.repositories.flatMap((entry) => {
if (!isRecord(entry)) {
return [];
}
const id = typeof entry.id === "number" ? String(entry.id) : asString(entry.id);
const fullName = asString(entry.fullName);
if (!id || !fullName) {
return [];
}
const manifestKindValue = entry.manifestKind;
const manifestKind: IntegrationRepoManifestKind = manifestKindValue === "agent-plugin" || manifestKindValue === "marketplace" || manifestKindValue === "plugin"View on GitHub (pinned to 2b7df46e8a)
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.
Example fix
// before
if (!response.ok) {
throw getRequestError(payload, response, `Failed to complete GitHub installation (${response.status}).`);
}
// after
if (!response.ok) {
if (response.status === 400 || response.status === 403) {
throw new Error("GitHub install session expired - restart the installation from the integrations page.");
}
throw getRequestError(payload, response, `Failed to complete GitHub installation (${response.status}).`);
} Defensive patterns
Strategy: retry
Validate before calling
// state must be fresh (minted by useStartGithubInstall moments ago) if (!input.installationId || !input.state) return; // incomplete callback params from GitHub if (completionAttempted) return; // state is single-use; never POST twice
Type guard
function hasCompletionInput(v: unknown): v is { installationId: string; state: string } {
return typeof v === "object" && v !== null
&& typeof (v as any).installationId === "string"
&& typeof (v as any).state === "string";
} Try / catch
try {
const result = await completeGithubInstall(input);
} catch (error) {
if (isReauthRequiredError(error)) { promptReauth(); return; }
if (error instanceof Error && /state|expired|400|403/i.test(error.message)) {
restartInstallFlow(); // mint a fresh state token
return;
}
setConnectError(error instanceof Error ? error.message : "Could not complete GitHub installation.");
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to start GitHub install (${response.status}).
- Failed to fetch latest-mac.yml (${response.status} ${respons
- plugin_ref_not_found
- Managed MCP outbound request exceeded the guarded redirect l
- OIDC discovery failed with ${response.status}. Enter manual
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/b7b3584a9a60b936.
Report an issue: GitHub.