{"record":{"id":"72797fb622ec3056","repo":"paperclipai/paperclip","slug":"github-did-not-confirm-the-expected-secure-papercl","errorCode":null,"errorMessage":"GitHub did not confirm the expected secure Paperclip webhook. Reconnect to retry.","messagePattern":"GitHub did not confirm the expected secure Paperclip webhook\\. Reconnect to retry\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/chat-github-webhook-config.ts","lineNumber":776,"sourceCode":"    const parsed: unknown = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n    if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n      throw new Error(\"Invalid webhook configuration response\");\n    }\n    config = parsed as Record<string, unknown>;\n  } catch {\n    await reader?.cancel().catch(() => undefined);\n    throw new Error(\n      \"GitHub returned an unreadable webhook configuration. Reconnect to confirm the callback settings.\",\n    );\n  } finally {\n    reader?.releaseLock();\n  }\n  if (\n    config.url !== input.webhookUrl ||\n    config.content_type !== \"json\" ||\n    (config.insecure_ssl !== \"0\" && config.insecure_ssl !== 0)\n  ) {\n    throw new Error(\n      \"GitHub did not confirm the expected secure Paperclip webhook. Reconnect to retry.\",\n    );\n  }\n}\n","sourceCodeStart":758,"sourceCodeEnd":781,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-github-webhook-config.ts#L758-L781","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Reconnect the GitHub connection so Paperclip re-provisions the webhook to the expected URL/content-type/SSL settings.","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).","If the server's public base URL changed, update it and reconnect so webhookUrl matches what GitHub stores.","Ensure no other tool or team member is editing the App webhook concurrently."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-check the App's webhook via GitHub settings or API before resync:\nconst cfg = await gh.request('GET /app/hook');\nconst expected = new URL(webhookUrl);\nconst actual = new URL(cfg.data.config.url);\nif (actual.origin !== expected.origin || cfg.data.config.content_type !== 'json' || cfg.data.config.insecure_ssl !== '0') {\n  await promptReconnect(); // fix webhook before resync\n}","typeGuard":"function isExpectedWebhookConfig(c: unknown, url: string): boolean {\n  const w = c as { url?: string; content_type?: string; insecure_ssl?: string | number } | null;\n  return !!w && w.url === url && w.content_type === 'json' && (w.insecure_ssl === '0' || w.insecure_ssl === 0);\n}","tryCatchPattern":"try { await resyncGitHubAppWebhook(input); }\ncatch (e) {\n  if (e.message.includes('did not confirm')) {\n    // webhook was changed out-of-band; trigger full re-provision via reconnect\n    await reconnectGitHubApp(connectionId);\n  }\n}","preventionTips":["Restrict who/what can edit the App's webhook settings.","Keep the server's public base URL stable; reconnect after any hostname change.","Always provision with content_type json and SSL verification enabled.","Reconcile webhook settings after manual App configuration."],"tags":["github","webhook","config-mismatch","ssl"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}