{"record":{"id":"17f666a3b13d3b7d","repo":"different-ai/openwork","slug":"github-connector-request-failed","errorCode":"github_connector_request_failed","errorMessage":"${error.message}","messagePattern":"\\$\\{error\\.message\\}","errorType":"http","errorClass":"PluginArchRouteFailure","httpStatus":409,"severity":"error","filePath":"ee/apps/den-api/src/routes/org/plugin-system/store.ts","lineNumber":4357,"sourceCode":"export function consumeGithubInstallState(state: string) {\n  const parsed = verifyGithubInstallStateToken({ secret: env.betterAuthSecret, token: state })\n  if (!parsed) {\n    throw new PluginArchRouteFailure(400, \"invalid_github_install_state\", \"GitHub install state is invalid or expired.\")\n  }\n  return parsed\n}\n\nfunction wrapGithubConnectorError(error: unknown): never {\n  if (error instanceof PluginArchRouteFailure) {\n    throw error\n  }\n\n  if (error instanceof GithubConnectorConfigError) {\n    throw new PluginArchRouteFailure(409, \"github_connector_app_not_configured\", error.message)\n  }\n\n  if (error instanceof GithubConnectorRequestError) {\n    throw new PluginArchRouteFailure(409, \"github_connector_request_failed\", error.message, { cause: error })\n  }\n\n  throw error\n}\n\nfunction normalizeDiscoveryCursor(value: string | undefined) {\n  return value?.trim() || undefined\n}\n\nfunction discoveryStep(status: GithubConnectorDiscoveryStep[\"status\"], id: GithubConnectorDiscoveryStep[\"id\"], label: string): GithubConnectorDiscoveryStep {\n  return { id, label, status }\n}\n\nfunction buildGithubConnectorDiscoverySteps(input: {\n  classification: GithubDiscoveryClassification\n  discoveredPlugins: GithubDiscoveredPlugin[]\n}) {\n  return [","sourceCodeStart":4339,"sourceCodeEnd":4375,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-api/src/routes/org/plugin-system/store.ts#L4339-L4375","documentation":"wrapGithubConnectorError converts GithubConnectorRequestError into a 409 github_connector_request_failed with the underlying message attached as cause. This means a request to GitHub's API from the connector failed — network error, 4xx/5xx from GitHub, or timeout — not a local config problem. The original error is preserved for server-side inspection.","triggerScenarios":"GitHub connector operations (token exchange, installation lookup, API calls) where fetch to github.com failed: network unreachable, GitHub returned an error status, TLS issues, or rate limiting surfaced as GithubConnectorRequestError.","commonSituations":"Egress firewall blocking api.github.com from the server; GitHub App credentials revoked causing 401/404 from GitHub; GitHub rate limits during bulk install syncs; transient GitHub incidents.","solutions":["Read error.message/cause to see the GitHub-side status or network error and address it specifically","Check server egress connectivity to https://api.github.com and https://github.com (proxy/firewall rules)","Verify the GitHub App still exists, is not suspended, and credentials are valid","Retry after backoff for transient 5xx/rate-limit responses; check the GitHub status page"],"exampleFix":"// before\nawait exchangeCodeForToken(code) // raw failure propagates as 409\n// after\ntry {\n  await exchangeCodeForToken(code)\n} catch (e) {\n  if (isGithubConnectorRequestError(e) && e.status === 403 && e.rateLimited) {\n    await sleep(backoffFor(e.resetAt)); return exchangeCodeForToken(code)\n  }\n  throw e\n}","handlingStrategy":"retry","validationCode":"// cheap reachability pre-check for egress-restricted environments\nconst ok = await fetch(\"https://api.github.com/zen\", { signal: AbortSignal.timeout(5000) })\n  .then((r) => r.ok).catch(() => false)\nif (!ok) throw new Error(\"GitHub API unreachable from server — check egress/proxy\")","typeGuard":"function isGithubConnectorRequestError(e: unknown): boolean {\n  return e instanceof PluginArchRouteFailure && e.code === \"github_connector_request_failed\"\n}\nfunction isGithubConnectorRequestErrorRaw(e: unknown): e is GithubConnectorRequestError {\n  return e instanceof GithubConnectorRequestError\n}","tryCatchPattern":"try {\n  return await githubConnectorOperation(args)\n} catch (e) {\n  if (isGithubConnectorRequestError(e)) {\n    log.error(\"github request failed\", { cause: e.cause })\n    if (isRetryable(e.cause)) return retryWithBackoff(() => githubConnectorOperation(args))\n    return respond(502, \"GitHub request failed: \" + e.message)\n  }\n  throw e\n}","preventionTips":["Allow-list egress to api.github.com and github.com from the server network","Inspect the wrapped cause for GitHub status codes and handle 401/404/403 (revoked app, rate limit) distinctly","Back off on 5xx/429 with jittered retries and a retry cap","Subscribe to GitHub status and alert on correlated request-failure spikes"],"tags":["github-api","network","rate-limit","http-409"],"backgroundTag":"upstream-request-failed","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}