paperclipai/paperclip · error
PAPERCLIP_API_URL is required to create a routine webhook
Error message
PAPERCLIP_API_URL is required to create a routine webhook
What it means
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.
Source
Thrown at server/src/services/routines.ts:108
"issue.read_marked",
"issue.read_unmarked",
"issue.inbox_archived",
"issue.inbox_unarchived",
"issue.inbox_touched",
];
const WEEKDAY_INDEX: Record<string, number> = {
Sun: 0,
Mon: 1,
Tue: 2,
Wed: 3,
Thu: 4,
Fri: 5,
Sat: 6,
};
export function routineWebhookUrl(publicId: string): string {
const baseUrl = runtimePublicOrigin() ?? process.env.PAPERCLIP_API_URL?.trim();
if (!baseUrl) throw new Error("PAPERCLIP_API_URL is required to create a routine webhook");
return `${baseUrl.replace(/\/+$/, "")}/api/routine-triggers/public/${publicId}/fire`;
}
type ExecutionIssueTransientFailureStatus = (typeof EXECUTION_ISSUE_TRANSIENT_FAILURE_STATUSES)[number];
function executionIssueTransientFailureReason(status: ExecutionIssueTransientFailureStatus) {
return `Execution issue moved to ${status}`;
}
function executionIssueTransientFailureStatusFromPayload(payload: unknown): ExecutionIssueTransientFailureStatus | null {
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return null;
const transientFailure = (payload as Record<string, unknown>).transientFailure;
if (!transientFailure || typeof transientFailure !== "object" || Array.isArray(transientFailure)) return null;
const record = transientFailure as Record<string, unknown>;
if (record.code !== EXECUTION_ISSUE_TRANSIENT_FAILURE_CODE) return null;
return EXECUTION_ISSUE_TRANSIENT_FAILURE_STATUSES.find((status) => record.status === status) ?? null;
}
View on GitHub (pinned to 01ad858492)
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
Example fix
// before: running without env config pnpm dev # PAPERCLIP_API_URL unset -> throws on routine webhook creation // after export PAPERCLIP_API_URL="https://api.myinstance.dev" pnpm dev
Defensive patterns
Strategy: try-catch
Validate before calling
const baseUrl = process.env.PAPERCLIP_API_URL?.trim();
if (!baseUrl) throw new Error('PAPERCLIP_API_URL must be set to create routine webhooks');
new URL('/api/routine-triggers/public/x/fire', baseUrl); // also validates format Try / catch
try {
const url = routineWebhookUrl(publicId);
} catch (e) {
if ((e as Error).message.includes('PAPERCLIP_API_URL is required')) {
// surface a clear config error to the operator, not a 500
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- OpenCode evals require exact version 1.18.17; received ${ver
- devUiUrl must use http or https protocol
- devUiUrl must target localhost
- Plugin tool dispatch is not enabled
- Existing Anthropic Agent model does not match the requested
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/5f89afca77f7779a.
Report an issue: GitHub.