paperclipai/paperclip · error · GitHubWebhookRecoveryError

github_webhook_recovery_transport

github_webhook_recovery_transport

Error message

github_webhook_recovery_transport

What it means

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.

Source

Thrown at server/src/services/chat-github-webhook-config.ts:355

      (async () => {
        const response = await input.fetch(
          `https://api.github.com${input.path}`,
          {
            method: input.method ?? "GET",
            redirect: "error",
            signal: controller.signal,
            headers: {
              accept: "application/vnd.github+json",
              authorization: `Bearer ${input.token}`,
              "x-github-api-version": "2022-11-28",
              ...(input.body ? { "content-type": "application/json" } : {}),
            },
            ...(input.body ? { body: input.body } : {}),
          },
        );
        if (controller.signal.aborted) {
          await response.body?.cancel().catch(() => undefined);
          throw new GitHubWebhookRecoveryError(
            "github_webhook_recovery_transport",
            null,
            null,
            input.uncertainMutation === true,
          );
        }
        if (response.status !== (input.expectedStatus ?? 200)) {
          await response.body?.cancel().catch(() => undefined);
          throw new GitHubWebhookRecoveryError(
            "github_webhook_recovery_http",
            response.status,
            retryDelay(response.headers),
            input.uncertainMutation === true &&
              (response.status >= 500 || response.status === 408),
          );
        }
        return input.project(response, controller.signal);
      })(),

View on GitHub (pinned to 01ad858492)

Solutions

  1. Check https://www.githubstatus.com for a GitHub API incident; wait and retry if degraded.
  2. 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).
  3. For requestGitHubAppWebhookRedelivery, treat transport errors as ambiguous: check delivery status via getGitHubAppWebhookDelivery before re-triggering, since the mutation may have been accepted.
  4. Retry with backoff for GET endpoints (safe); never blind-retry POST redeliveries.
  5. Verify network/proxy health and DNS resolution to api.github.com.

Example fix

// before
await requestGitHubAppWebhookRedelivery({ fetch, appToken, deliveryId }); // may throw transport error
// after
try {
  await requestGitHubAppWebhookRedelivery({ fetch, appToken, deliveryId });
} catch (e) {
  if (e instanceof GitHubWebhookRecoveryError && e.code === "github_webhook_recovery_transport" && e.requestMayHaveBeenAccepted) {
    const d = await getGitHubAppWebhookDelivery({ fetch, appToken, deliveryId }); // reconcile before retrying
    if (d.redelivery) return; // already redelivered
  } else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

function isTransportTimeout(e: unknown): e is GitHubWebhookRecoveryError {
  return e instanceof GitHubWebhookRecoveryError && e.code === "github_webhook_recovery_transport";
}

Try / catch

try {
  await requestGitHubAppWebhookRedelivery({ fetch, appToken, deliveryId });
} catch (e) {
  if (e instanceof GitHubWebhookRecoveryError && e.code === "github_webhook_recovery_transport") {
    // requestMayHaveBeenAccepted may be true — reconcile, don't blind-retry
    const d = await getGitHubAppWebhookDelivery({ fetch, appToken, deliveryId });
    if (!d.redelivery) await requestGitHubAppWebhookRedelivery({ fetch, appToken, deliveryId });
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/838181b24492f6f0. Report an issue: GitHub.