paperclipai/paperclip · error · Error
Invalid PAPERCLIP_INSTANCE_ID '${instanceId}'.
Error message
Invalid PAPERCLIP_INSTANCE_ID '${instanceId}'. What it means
Thrown by resolvePaperclipInstanceRootForAdapter when the resolved instance id fails PATH_SEGMENT_RE (/^[a-zA-Z0-9_-]+$/). The instance id becomes a directory name under <home>/instances/<id>, so any character outside letters, digits, underscore, or hyphen is rejected to prevent path traversal and filesystem-illegal names. It reads from input.instanceId, then env PAPERCLIP_INSTANCE_ID, then defaults to 'default'.
Source
Thrown at packages/adapter-utils/src/server-utils.ts:154
const MATERIALIZED_SKILL_LOCK_OWNER = "owner.json";
const MATERIALIZED_SKILL_LOCK_STALE_MS = 30_000;
function expandHomePrefix(value: string): string {
if (value === "~") return os.homedir();
if (value.startsWith("~/")) return path.resolve(os.homedir(), value.slice(2));
return value;
}
export function resolvePaperclipInstanceRootForAdapter(input: {
homeDir?: string;
instanceId?: string;
env?: NodeJS.ProcessEnv;
} = {}): string {
const env = input.env ?? process.env;
const homeRaw = input.homeDir?.trim() || env.PAPERCLIP_HOME?.trim();
const homeDir = path.resolve(homeRaw ? expandHomePrefix(homeRaw) : path.resolve(os.homedir(), ".paperclip"));
const instanceId = input.instanceId?.trim() || env.PAPERCLIP_INSTANCE_ID?.trim() || DEFAULT_PAPERCLIP_INSTANCE_ID;
if (!PATH_SEGMENT_RE.test(instanceId)) throw new Error(`Invalid PAPERCLIP_INSTANCE_ID '${instanceId}'.`);
return path.resolve(homeDir, "instances", instanceId);
}
export const DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE = [
"You are agent {{agent.id}} ({{agent.name}}). Continue your Paperclip work.",
"",
"Execution contract:",
"- Start actionable work in this heartbeat; do not stop at a plan unless the issue asks for planning.",
"- Leave durable progress in comments, documents, or work products, then update the issue to a clear final disposition before ending the heartbeat.",
"- Comments, documents, screenshots, work products, and `Remaining` bullets are evidence, not valid liveness paths by themselves.",
"- Final disposition checklist: mark `done` when complete; use `in_review` only with a real reviewer, approval, interaction, or monitor path; use `blocked` only with first-class blockers or a named unblock owner/action; create delegated follow-up issues with blockers when another agent owns the next step; keep `in_progress` only when a live continuation path exists.",
"- Prefer the smallest verification that proves the change; do not default to full workspace typecheck/build/test on every heartbeat unless the task scope warrants it.",
"- After 2 consecutive failures of the same control-plane write, stop retrying that write for the rest of the heartbeat. Continue useful work, report the failure in the final response, and rely on the adapter/runtime status channel as the sanctioned fallback.",
"- Use child issues for parallel or long delegated work instead of polling agents, sessions, or processes.",
"- If woken by a human comment on a dependency-blocked issue, respond or triage the comment without treating the blocked deliverable work as unblocked.",
"- Create child issues directly when you know what needs to be done; use issue-thread interactions when the board/user must choose suggested tasks, answer structured questions, or confirm a proposal.",
"- Use `PAPERCLIP_SCRATCH_DIR` / `PAPERCLIP_RUN_SCRATCH_DIR` for temporary scratch files instead of ad hoc `/tmp` paths; Paperclip removes that run-owned directory after the run ends.",
"- To ask for that input, create an interaction on the current issue with POST /api/issues/{issueId}/interactions using kind suggest_tasks, ask_user_questions, or request_confirmation. Use continuationPolicy wake_assignee when you need to resume after a response (it wakes on acceptance and rejection alike; only expiry does not wake); use wake_assignee_on_accept when you want to resume only after acceptance.",View on GitHub (pinned to 67001ec6eb)
Solutions
- Set PAPERCLIP_INSTANCE_ID to a slug of only [A-Za-z0-9_-], e.g. 'prod-v2'.
- Validate the id with PATH_SEGMENT_RE-shaped regex (/^[A-Za-z0-9_-]+$/) in your deployment config before launching the adapter.
- Leave it unset to use the 'default' instance if you only run one instance.
- If you need hierarchy, encode it with '-' not '/' or '.'.
Example fix
// before PAPERCLIP_INSTANCE_ID=acme.io/prod // after PAPERCLIP_INSTANCE_ID=acme-prod
Defensive patterns
Strategy: validation
Validate before calling
const INSTANCE_ID_RE = /^[A-Za-z0-9_-]+$/;
function resolveInstanceId(raw: string | undefined, env: NodeJS.ProcessEnv): string {
const id = (raw ?? env.PAPERCLIP_INSTANCE_ID ?? '').trim() || 'default';
if (!INSTANCE_ID_RE.test(id)) throw new Error(`Invalid instance id: '${id}'`);
return id;
} Type guard
function isValidInstanceId(id: string): boolean {
return typeof id === 'string' && /^[A-Za-z0-9_-]+$/.test(id);
} Prevention
- Constrain PAPERCLIP_INSTANCE_ID to [A-Za-z0-9_-] in deployment configs.
- Encode hierarchy with '-', not '/' or '.'.
- Default to unset ('default') for single-instance installs.
When it happens
Trigger: resolvePaperclipInstanceRootForAdapter called with instanceId (or PAPERCLIP_INSTANCE_ID env) containing slashes, dots, spaces, colons, or any non-[A-Za-z0-9_-] char. Common with multi-tenant ids, UUIDs with braces, or accidental 'production/v2' style values.
Common situations: Operator sets PAPERCLIP_INSTANCE_ID='acme.io' (dot) or 'prod v2' (space); a deployment script interpolates a URL or path into the env var; an id with a slash used to denote hierarchy; default empty string after trimming.
Related errors
- Company ID is required. Pass --company-id, set PAPERCLIP_COM
- Source and target Paperclip configs are the same. Pass --fro
- Resolved worktree path ${directPath} does not contain .paper
- Resolved worktree "${selector}" does not look like a Papercl
- Hostname is required
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/256962de670b243a.
Report an issue: GitHub.