{"record":{"id":"f00bb96d671b9cdc","repo":"paperclipai/paperclip","slug":"paperclip-chat-webhook-public-url-must-be-an-https-origin","errorCode":null,"errorMessage":"PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL must be an HTTPS origin without credentials, a path, query, or fragment","messagePattern":"PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL must be an HTTPS origin without credentials, a path, query, or fragment","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/chat-webhook-public-url.ts","lineNumber":21,"sourceCode":"  value: string | null | undefined,\n): string | undefined {\n  if (!value?.trim()) return undefined;\n  try {\n    const url = new URL(value.trim());\n    if (\n      url.protocol === \"https:\" &&\n      url.hostname &&\n      !url.username &&\n      !url.password &&\n      url.pathname === \"/\" &&\n      !url.search &&\n      !url.hash\n    )\n      return url.origin;\n  } catch {\n    // Never include an operator-supplied URL: it could contain credentials.\n  }\n  throw new Error(\n    \"PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL must be an HTTPS origin without credentials, a path, query, or fragment\",\n  );\n}\n","sourceCodeStart":3,"sourceCodeEnd":25,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/chat-webhook-public-url.ts#L3-L25","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL to a bare HTTPS origin only, e.g. https://abc123.ngrok.io (no path, query, credentials, or fragment)","If you need a path, configure it in the webhook route settings instead of the base URL env var","Check for trailing slashes, quotes, or whitespace copied into the env value","Ensure your tunnel/proxy terminates TLS so the public URL is genuinely HTTPS"],"exampleFix":"// before\nPAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL=https://tunnel.example.com/webhook?key=abc\n// after\nPAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL=https://tunnel.example.com","handlingStrategy":"validation","validationCode":"const raw = process.env.PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL;\nfunction isValidPublicOrigin(u?: string): boolean {\n  if (!u) return false;\n  try { const url = new URL(u);\n    return url.protocol === \"https:\" && !url.username && !url.password\n      && url.pathname === \"/\" && !url.search && !url.hash;\n  } catch { return false; }\n}\nif (!isValidPublicOrigin(raw)) throw new Error(\"PAPERCLIP_CHAT_WEBHOOK_PUBLIC_URL must be a bare HTTPS origin\");","typeGuard":"function isValidWebhookPublicUrl(v: unknown): v is string {\n  if (typeof v !== \"string\") return false;\n  try { const u = new URL(v);\n    return u.protocol === \"https:\" && !u.username && !u.password && u.pathname === \"/\" && !u.search && !u.hash;\n  } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Never append a path, query, or credentials to the webhook public URL env var","Validate the env var with a startup config check before deploying","Prefer tunnel providers that give stable HTTPS origins (ngrok, Cloudflare Tunnel)","Strip trailing slashes and surrounding quotes when copying the URL into config"],"tags":["env-var","https","config","url"],"backgroundTag":"invalid-url-format","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}