paperclipai/paperclip · error

PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL must be an HTTPS origin…

Error message

PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL must be an HTTPS origin without credentials, a path, query, or fragment

What it means

parseChatWebhookPublicBaseUrl validates the PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL environment variable. It requires an HTTPS origin with no credentials, path, query, or fragment because the value is used as a public webhook base origin. If URL parsing fails or any constraint is violated, it throws this error and deliberately omits the offending URL from the message so credentials never leak into logs.

Solutions

  1. Set PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL to a bare HTTPS origin only, e.g. https://abc123.ngrok.io (no path, query, credentials, or fragment)
  2. If you need a path, configure it in the webhook route settings instead of the base URL env var
  3. Check for trailing slashes, quotes, or whitespace copied into the env value
  4. Ensure your tunnel/proxy terminates TLS so the public URL is genuinely HTTPS

Example fix

// before
PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL=https://tunnel.example.com/webhook?key=abc
// after
PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL=https://tunnel.example.com
Defensive patterns

Strategy: validation

Validate before calling

const raw = process.env.PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL;
function isValidPublicOrigin(u?: string): boolean {
  if (!u) return false;
  try { const url = new URL(u);
    return url.protocol === "https:" && !url.username && !url.password
      && url.pathname === "/" && !url.search && !url.hash;
  } catch { return false; }
}
if (!isValidPublicOrigin(raw)) throw new Error("PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL must be a bare HTTPS origin");

Type guard

function isValidWebhookPublicUrl(v: unknown): v is string {
  if (typeof v !== "string") return false;
  try { const u = new URL(v);
    return u.protocol === "https:" && !u.username && !u.password && u.pathname === "/" && !u.search && !u.hash;
  } catch { return false; }
}

Prevention

When it happens

Trigger: Setting PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL to an http:// URL, a URL with embedded user:pass credentials, a URL containing a path (/webhook), query (?x=1), or fragment (#frag), or any value that fails `new URL()` parsing; invoked by loadConfig and webhookPublicBaseUrl at startup.

Common situations: Operators pasting a full tunnel/ngrok forwarding URL like https://xyz.ngrok.io/webhook instead of just the origin; using http:// for a local dev tunnel; typos or whitespace in the env var making the URL unparseable.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/f00bb96d671b9cdc. Report an issue: GitHub.

Appendix: source

Thrown at server/src/chat-webhook-public-url.ts:21

  value: string | null | undefined,
): string | undefined {
  if (!value?.trim()) return undefined;
  try {
    const url = new URL(value.trim());
    if (
      url.protocol === "https:" &&
      url.hostname &&
      !url.username &&
      !url.password &&
      url.pathname === "/" &&
      !url.search &&
      !url.hash
    )
      return url.origin;
  } catch {
    // Never include an operator-supplied URL: it could contain credentials.
  }
  throw new Error(
    "PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL must be an HTTPS origin without credentials, a path, query, or fragment",
  );
}

View on GitHub (pinned to 3f1d897a7c)