paperclipai/paperclip · error

GitHub webhook configuration is incomplete

Error message

GitHub webhook configuration is incomplete

What it means

resyncGitHubAppWebhook validates the webhook URL and secret before PATCHing https://api.github.com/app/hook/config, throwing a plain Error('GitHub webhook configuration is incomplete') if the URL is not a clean https URL (no credentials/query/hash) or the webhookSecret is empty. This is a local configuration guard — no network request is made.

Source

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

 * A successful PATCH is configuration evidence only, never a signed ping or a
 * successful chat round trip. See https://docs.github.com/en/rest/apps/webhooks.
 */
export async function resyncGitHubAppWebhook(input: {
  fetch: typeof globalThis.fetch;
  appToken: string;
  webhookUrl: string;
  webhookSecret: string;
}): Promise<void> {
  const webhookUrl = new URL(input.webhookUrl);
  if (
    webhookUrl.protocol !== "https:" ||
    webhookUrl.username ||
    webhookUrl.password ||
    webhookUrl.search ||
    webhookUrl.hash ||
    !input.webhookSecret
  ) {
    throw new Error("GitHub webhook configuration is incomplete");
  }

  let response: Response;
  try {
    response = await input.fetch(GITHUB_APP_WEBHOOK_CONFIG_URL, {
      method: "PATCH",
      redirect: "error",
      signal: AbortSignal.timeout(25_000),
      headers: {
        accept: "application/vnd.github+json",
        authorization: `Bearer ${input.appToken}`,
        "content-type": "application/json",
        "x-github-api-version": "2022-11-28",
      },
      body: JSON.stringify({
        url: input.webhookUrl,
        content_type: "json",
        insecure_ssl: "0",

View on GitHub (pinned to 01ad858492)

Solutions

  1. Supply an https:// URL with no query string, fragment, or embedded credentials — put any token in the path instead.
  2. Provision the webhook secret: check the env var/secret manager is set and non-empty before calling resync.
  3. Normalize the URL: strip trailing query/hash, ensure scheme is https, then pass the cleaned value.
  4. Fail fast in your own startup code with a clear message if webhookUrl/webhookSecret are missing or non-https.

Example fix

// before
await resyncGitHubAppWebhook({ fetch, appToken, webhookUrl: process.env.WEBHOOK_URL, webhookSecret: process.env.WEBHOOK_SECRET });
// after
const webhookUrl = process.env.WEBHOOK_URL;
const webhookSecret = process.env.WEBHOOK_SECRET;
const u = new URL(webhookUrl);
if (u.protocol !== "https:" || u.search || u.hash || u.username || u.password || !webhookSecret) {
  throw new Error("WEBHOOK_URL must be a clean https URL and WEBHOOK_SECRET must be set");
}
await resyncGitHubAppWebhook({ fetch, appToken, webhookUrl, webhookSecret });
Defensive patterns

Strategy: validation

Validate before calling

function validateWebhookConfig(webhookUrl: string, webhookSecret: string): void {
  const u = new URL(webhookUrl);
  if (u.protocol !== "https:" || u.username || u.password || u.search || u.hash) throw new Error("webhookUrl must be a clean https URL");
  if (!webhookSecret) throw new Error("webhookSecret is required");
}

Type guard

null

Try / catch

try {
  await resyncGitHubAppWebhook({ fetch, appToken, webhookUrl, webhookSecret });
} catch (e) {
  if (e instanceof Error && e.message === "GitHub webhook configuration is incomplete") {
    throw new ConfigError("Set WEBHOOK_URL (https, no query/hash/credentials) and WEBHOOK_SECRET before resync");
  }
  throw e;
}

Prevention

When it happens

Trigger: webhookUrl passed as http://, with an embedded user:pass, with ?query or #fragment, or not a parseable URL (new URL throws before the check); webhookSecret empty string, undefined, or a placeholder not yet configured.

Common situations: Dev environment running with http://localhost callback (not https); secret not provisioned from env/secret manager (empty GITHUB_APP_WEBHOOK_SECRET); a tunneling URL like https://tunnel.example/cb?token=xyz containing a query string; forgetting to URL-encode/remove fragments.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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