{"record":{"id":"5f89afca77f7779a","repo":"paperclipai/paperclip","slug":"paperclip-api-url-is-required-to-create-a-routine","errorCode":null,"errorMessage":"PAPERCLIP_API_URL is required to create a routine webhook","messagePattern":"PAPERCLIP_API_URL is required to create a routine webhook","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/routines.ts","lineNumber":108,"sourceCode":"  \"issue.read_marked\",\n  \"issue.read_unmarked\",\n  \"issue.inbox_archived\",\n  \"issue.inbox_unarchived\",\n  \"issue.inbox_touched\",\n];\nconst WEEKDAY_INDEX: Record<string, number> = {\n  Sun: 0,\n  Mon: 1,\n  Tue: 2,\n  Wed: 3,\n  Thu: 4,\n  Fri: 5,\n  Sat: 6,\n};\n\nexport function routineWebhookUrl(publicId: string): string {\n  const baseUrl = runtimePublicOrigin() ?? process.env.PAPERCLIP_API_URL?.trim();\n  if (!baseUrl) throw new Error(\"PAPERCLIP_API_URL is required to create a routine webhook\");\n  return `${baseUrl.replace(/\\/+$/, \"\")}/api/routine-triggers/public/${publicId}/fire`;\n}\n\ntype ExecutionIssueTransientFailureStatus = (typeof EXECUTION_ISSUE_TRANSIENT_FAILURE_STATUSES)[number];\n\nfunction executionIssueTransientFailureReason(status: ExecutionIssueTransientFailureStatus) {\n  return `Execution issue moved to ${status}`;\n}\n\nfunction executionIssueTransientFailureStatusFromPayload(payload: unknown): ExecutionIssueTransientFailureStatus | null {\n  if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) return null;\n  const transientFailure = (payload as Record<string, unknown>).transientFailure;\n  if (!transientFailure || typeof transientFailure !== \"object\" || Array.isArray(transientFailure)) return null;\n  const record = transientFailure as Record<string, unknown>;\n  if (record.code !== EXECUTION_ISSUE_TRANSIENT_FAILURE_CODE) return null;\n  return EXECUTION_ISSUE_TRANSIENT_FAILURE_STATUSES.find((status) => record.status === status) ?? null;\n}\n","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/routines.ts#L90-L126","documentation":"routineWebhookUrl builds the public webhook URL for a routine trigger from a public base origin: the configured runtime public origin, falling back to the PAPERCLIP_API_URL env var. If neither is set there is no way to construct an externally reachable URL, so it throws. Webhooks must point at a URL the external scheduler can reach, so a guess is unsafe.","triggerScenarios":"Creating or updating a routine with a webhook trigger while PAPERCLIP_API_URL is unset (or empty/whitespace) and no runtime public origin is configured — typical in fresh dev checkouts, container images without env wiring, or tests.","commonSituations":"Local dev without the env var set; docker-compose/K8s manifest missing PAPERCLIP_API_URL; env var present but empty string or only whitespace; a hosted deployment where only an internal URL was configured.","solutions":["Set PAPERCLIP_API_URL to the externally reachable base URL (e.g. https://api.example.com) and restart","Verify the value has no surrounding whitespace and no trailing path issues; the code trims and strips trailing slashes","If embedding, configure the runtime public origin so the env var is unnecessary","In tests, inject a base URL explicitly instead of relying on ambient env"],"exampleFix":"// before: running without env config\npnpm dev  # PAPERCLIP_API_URL unset -> throws on routine webhook creation\n// after\nexport PAPERCLIP_API_URL=\"https://api.myinstance.dev\"\npnpm dev","handlingStrategy":"try-catch","validationCode":"const baseUrl = process.env.PAPERCLIP_API_URL?.trim();\nif (!baseUrl) throw new Error('PAPERCLIP_API_URL must be set to create routine webhooks');\nnew URL('/api/routine-triggers/public/x/fire', baseUrl); // also validates format","typeGuard":null,"tryCatchPattern":"try {\n  const url = routineWebhookUrl(publicId);\n} catch (e) {\n  if ((e as Error).message.includes('PAPERCLIP_API_URL is required')) {\n    // surface a clear config error to the operator, not a 500\n  } else throw e;\n}","preventionTips":["Set PAPERCLIP_API_URL in every environment where routines with webhooks are created","Validate the env var at startup (fail fast) instead of at webhook creation","Use the externally reachable origin, not an internal container hostname","Trim the value; the code trims and strips trailing slashes itself"],"tags":["configuration","webhook","env"],"backgroundTag":"missing-env-var","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"}