{"record":{"id":"838181b24492f6f0","repo":"paperclipai/paperclip","slug":"github-webhook-recovery-transport","errorCode":"github_webhook_recovery_transport","errorMessage":"github_webhook_recovery_transport","messagePattern":"github_webhook_recovery_transport","errorType":"error_code","errorClass":"GitHubWebhookRecoveryError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-github-webhook-config.ts","lineNumber":355,"sourceCode":"      (async () => {\n        const response = await input.fetch(\n          `https://api.github.com${input.path}`,\n          {\n            method: input.method ?? \"GET\",\n            redirect: \"error\",\n            signal: controller.signal,\n            headers: {\n              accept: \"application/vnd.github+json\",\n              authorization: `Bearer ${input.token}`,\n              \"x-github-api-version\": \"2022-11-28\",\n              ...(input.body ? { \"content-type\": \"application/json\" } : {}),\n            },\n            ...(input.body ? { body: input.body } : {}),\n          },\n        );\n        if (controller.signal.aborted) {\n          await response.body?.cancel().catch(() => undefined);\n          throw new GitHubWebhookRecoveryError(\n            \"github_webhook_recovery_transport\",\n            null,\n            null,\n            input.uncertainMutation === true,\n          );\n        }\n        if (response.status !== (input.expectedStatus ?? 200)) {\n          await response.body?.cancel().catch(() => undefined);\n          throw new GitHubWebhookRecoveryError(\n            \"github_webhook_recovery_http\",\n            response.status,\n            retryDelay(response.headers),\n            input.uncertainMutation === true &&\n              (response.status >= 500 || response.status === 408),\n          );\n        }\n        return input.project(response, controller.signal);\n      })(),","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-github-webhook-config.ts#L337-L373","documentation":"Thrown when the fetch completed but the AbortController signal was already aborted — i.e. the request raced the timeout (default 25s, or the per-call timeoutMs like 10s/2s in getGitHubRecoveryComment). It carries code github_webhook_recovery_transport, and when uncertainMutation is true (redelivery POST) requestMayHaveBeenAccepted=true because GitHub may still have processed the mutation.","triggerScenarios":"GitHub API responding slower than the timeout during a redelivery POST or comment token mint; network stalls/proxy hangs; overload of api.github.com (degraded performance); timeoutMs set too low for a slow environment (e.g. 2s token revocation in getGitHubRecoveryComment timing out regularly).","commonSituations":"Corporate proxies adding seconds of latency; regional network issues to api.github.com; long redelivery queues on GitHub's side during incidents; containerized CI with slow DNS.","solutions":["Check https://www.githubstatus.com for a GitHub API incident; wait and retry if degraded.","Increase the timeout for calls you control by adjusting the timeoutMs passed through (recoveryRequest's callers fix these internally; in your wrapper allow longer budgets).","For requestGitHubAppWebhookRedelivery, treat transport errors as ambiguous: check delivery status via getGitHubAppWebhookDelivery before re-triggering, since the mutation may have been accepted.","Retry with backoff for GET endpoints (safe); never blind-retry POST redeliveries.","Verify network/proxy health and DNS resolution to api.github.com."],"exampleFix":"// before\nawait requestGitHubAppWebhookRedelivery({ fetch, appToken, deliveryId }); // may throw transport error\n// after\ntry {\n  await requestGitHubAppWebhookRedelivery({ fetch, appToken, deliveryId });\n} catch (e) {\n  if (e instanceof GitHubWebhookRecoveryError && e.code === \"github_webhook_recovery_transport\" && e.requestMayHaveBeenAccepted) {\n    const d = await getGitHubAppWebhookDelivery({ fetch, appToken, deliveryId }); // reconcile before retrying\n    if (d.redelivery) return; // already redelivered\n  } else throw e;\n}","handlingStrategy":"retry","validationCode":"null","typeGuard":"function isTransportTimeout(e: unknown): e is GitHubWebhookRecoveryError {\n  return e instanceof GitHubWebhookRecoveryError && e.code === \"github_webhook_recovery_transport\";\n}","tryCatchPattern":"try {\n  await requestGitHubAppWebhookRedelivery({ fetch, appToken, deliveryId });\n} catch (e) {\n  if (e instanceof GitHubWebhookRecoveryError && e.code === \"github_webhook_recovery_transport\") {\n    // requestMayHaveBeenAccepted may be true — reconcile, don't blind-retry\n    const d = await getGitHubAppWebhookDelivery({ fetch, appToken, deliveryId });\n    if (!d.redelivery) await requestGitHubAppWebhookRedelivery({ fetch, appToken, deliveryId });\n    return;\n  }\n  throw e;\n}","preventionTips":["Check githubstatus.com before running bulk recovery during incidents","Allow generous timeouts; the library already uses 25s default (2s for token revoke)","For POST mutations always reconcile state before retrying","Add backoff between recovery attempts"],"tags":["network","timeout","github-api","webhooks"],"backgroundTag":"request-timeout","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}