paperclipai/paperclip · error

GitHub did not confirm the expected secure Paperclip webhook

Error message

GitHub did not confirm the expected secure Paperclip webhook. Reconnect to retry.

What it means

Thrown by resyncGitHubAppWebhook after successfully parsing GitHub's webhook configuration response, when the config does not match what Paperclip provisioned: URL mismatch, content_type not 'json', or insecure_ssl not '0'/0. This is a post-condition verification that GitHub actually persisted the expected secure webhook settings; if not, the sync is considered failed.

Source

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

    const parsed: unknown = JSON.parse(Buffer.concat(chunks).toString("utf8"));
    if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
      throw new Error("Invalid webhook configuration response");
    }
    config = parsed as Record<string, unknown>;
  } catch {
    await reader?.cancel().catch(() => undefined);
    throw new Error(
      "GitHub returned an unreadable webhook configuration. Reconnect to confirm the callback settings.",
    );
  } finally {
    reader?.releaseLock();
  }
  if (
    config.url !== input.webhookUrl ||
    config.content_type !== "json" ||
    (config.insecure_ssl !== "0" && config.insecure_ssl !== 0)
  ) {
    throw new Error(
      "GitHub did not confirm the expected secure Paperclip webhook. Reconnect to retry.",
    );
  }
}

View on GitHub (pinned to 01ad858492)

Solutions

  1. Reconnect the GitHub connection so Paperclip re-provisions the webhook to the expected URL/content-type/SSL settings.
  2. Compare the App's webhook settings at github.com settings/apps with the expected webhookUrl and correct them manually (payload URL match, content type: application/json, SSL verification enabled).
  3. If the server's public base URL changed, update it and reconnect so webhookUrl matches what GitHub stores.
  4. Ensure no other tool or team member is editing the App webhook concurrently.
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check the App's webhook via GitHub settings or API before resync:
const cfg = await gh.request('GET /app/hook');
const expected = new URL(webhookUrl);
const actual = new URL(cfg.data.config.url);
if (actual.origin !== expected.origin || cfg.data.config.content_type !== 'json' || cfg.data.config.insecure_ssl !== '0') {
  await promptReconnect(); // fix webhook before resync
}

Type guard

function isExpectedWebhookConfig(c: unknown, url: string): boolean {
  const w = c as { url?: string; content_type?: string; insecure_ssl?: string | number } | null;
  return !!w && w.url === url && w.content_type === 'json' && (w.insecure_ssl === '0' || w.insecure_ssl === 0);
}

Try / catch

try { await resyncGitHubAppWebhook(input); }
catch (e) {
  if (e.message.includes('did not confirm')) {
    // webhook was changed out-of-band; trigger full re-provision via reconnect
    await reconnectGitHubApp(connectionId);
  }
}

Prevention

When it happens

Trigger: Parsed config object has url !== input.webhookUrl, or content_type !== 'json', or insecure_ssl is neither '0' nor 0 — i.e. GitHub's stored webhook points elsewhere, uses form encoding, or allows insecure SSL.

Common situations: Another admin or automation edited the App's webhook after provisioning; a manual App setup flow left the webhook on form-urlencoded or insecure SSL; the webhookUrl Paperclip expects changed (hostname move, base-URL change) without re-provisioning.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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