{"record":{"id":"547fa2382edc7dd7","repo":"different-ai/openwork","slug":"github-install-start-response-was-incomplete","errorCode":null,"errorMessage":"GitHub install start response was incomplete.","messagePattern":"GitHub install start response was incomplete\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/integration-data.tsx","lineNumber":428,"sourceCode":"      await runReauthableAction(\"start-github-install\", async () => {\n      const { response, payload } = await requestJson(\n        \"/v1/connectors/github/install/start\",\n        {\n          method: \"POST\",\n          body: JSON.stringify({ returnPath: input.returnPath }),\n        },\n        15000,\n      );\n\n      if (!response.ok) {\n        throw getRequestError(payload, response, `Failed to start GitHub install (${response.status}).`);\n      }\n\n      const item = isRecord(payload) && isRecord(payload.item) ? payload.item : null;\n      const redirectUrl = item ? asString(item.redirectUrl) : null;\n      const state = item ? asString(item.state) : null;\n      if (!redirectUrl || !state) {\n        throw new Error(\"GitHub install start response was incomplete.\");\n      }\n\n        result = { redirectUrl, state };\n      });\n      if (!result) {\n        throw new Error(\"GitHub install start response was incomplete.\");\n      }\n      return result;\n    },\n    onSuccess: () => {\n      queryClient.invalidateQueries({ queryKey: integrationQueryKeys.all });\n    },\n  });\n}\n\nexport function useGithubInstallCompletion(input: { installationId: number | null; state: string | null }) {\n  const { runReauthableAction } = useOrgDashboard();\n","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/integration-data.tsx#L410-L446","documentation":"useStartGithubInstall's mutation starts a GitHub App installation: the server responds with an item containing redirectUrl and state. If either is missing/empty after isRecord/asString narrowing, this error is thrown inside the mutation, and a second identical error is thrown when the mutation result comes back null.","triggerScenarios":"POST to start install returns 200 but item is absent, item.redirectUrl is null/empty, or item.state is null/empty — e.g. the GitHub App is not fully configured so no install URL can be built, or an API shape change moved these fields.","commonSituations":"GitHub App setup incomplete on the server (missing app slug/client id so redirectUrl can't be generated); CSRF/state secret store misconfigured server-side; frontend/server version mismatch on the item schema.","solutions":["Verify the GitHub App is fully configured on the Den server (app slug, client ID, secret) so a redirect URL can be generated.","Inspect the raw start-install response to see whether item/redirectUrl/state are present.","Re-authenticate — some servers omit state when the session cannot mint a CSRF token.","Align frontend and server versions so the response matches { item: { redirectUrl, state } }."],"exampleFix":"// before\nconst item = isRecord(payload) && isRecord(payload.item) ? payload.item : null;\nconst redirectUrl = item ? asString(item.redirectUrl) : null;\n// after\nconst item = isRecord(payload) && isRecord(payload.item) ? payload.item : null;\nconst redirectUrl = item ? asString(item.redirectUrl) : null;\nif (!redirectUrl && isRecord(payload)) console.error(\"install start payload keys:\", Object.keys(payload));","handlingStrategy":"validation","validationCode":"// before starting install, verify the app is configured:\nconst cfg = await requestJson(\"/v1/connectors/github/config\", { method: \"GET\" }, 10000);\nconst ready = isRecord(cfg.payload) && typeof cfg.payload.appSlug === \"string\" && cfg.payload.appSlug.length > 0;\nif (!ready) throw new Error(\"GitHub App not configured on server.\");","typeGuard":"function hasInstallStart(p: unknown): p is { item: { redirectUrl: string; state: string } } {\n  if (typeof p !== \"object\" || p === null) return false;\n  const item = (p as { item?: unknown }).item;\n  return (\n    typeof item === \"object\" && item !== null &&\n    typeof (item as { redirectUrl?: unknown }).redirectUrl === \"string\" &&\n    typeof (item as { state?: unknown }).state === \"string\"\n  );\n}","tryCatchPattern":"try {\n  await startGithubInstall();\n} catch (e) {\n  setError(e instanceof Error ? e.message : \"Could not start GitHub install.\");\n}","preventionTips":["Verify GitHub App configuration (slug, client ID, secret) before exposing the install button.","Add a server test asserting the start-install response contains item.redirectUrl and item.state.","Keep the state secret store healthy (it must be able to mint CSRF state).","Log the raw response when validation fails."],"tags":["github-app","api-contract","install-flow"],"backgroundTag":"missing-response-field","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}