{"record":{"id":"256962de670b243a","repo":"paperclipai/paperclip","slug":"invalid-paperclip-instance-id-instanceid","errorCode":null,"errorMessage":"Invalid PAPERCLIP_INSTANCE_ID '${instanceId}'.","messagePattern":"Invalid PAPERCLIP_INSTANCE_ID '(.+?)'\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/server-utils.ts","lineNumber":154,"sourceCode":"const MATERIALIZED_SKILL_LOCK_OWNER = \"owner.json\";\nconst MATERIALIZED_SKILL_LOCK_STALE_MS = 30_000;\n\nfunction expandHomePrefix(value: string): string {\n  if (value === \"~\") return os.homedir();\n  if (value.startsWith(\"~/\")) return path.resolve(os.homedir(), value.slice(2));\n  return value;\n}\n\nexport function resolvePaperclipInstanceRootForAdapter(input: {\n  homeDir?: string;\n  instanceId?: string;\n  env?: NodeJS.ProcessEnv;\n} = {}): string {\n  const env = input.env ?? process.env;\n  const homeRaw = input.homeDir?.trim() || env.PAPERCLIP_HOME?.trim();\n  const homeDir = path.resolve(homeRaw ? expandHomePrefix(homeRaw) : path.resolve(os.homedir(), \".paperclip\"));\n  const instanceId = input.instanceId?.trim() || env.PAPERCLIP_INSTANCE_ID?.trim() || DEFAULT_PAPERCLIP_INSTANCE_ID;\n  if (!PATH_SEGMENT_RE.test(instanceId)) throw new Error(`Invalid PAPERCLIP_INSTANCE_ID '${instanceId}'.`);\n  return path.resolve(homeDir, \"instances\", instanceId);\n}\n\nexport const DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE = [\n  \"You are agent {{agent.id}} ({{agent.name}}). Continue your Paperclip work.\",\n  \"\",\n  \"Execution contract:\",\n  \"- Start actionable work in this heartbeat; do not stop at a plan unless the issue asks for planning.\",\n  \"- Leave durable progress in comments, documents, or work products, then update the issue to a clear final disposition before ending the heartbeat.\",\n  \"- Comments, documents, screenshots, work products, and `Remaining` bullets are evidence, not valid liveness paths by themselves.\",\n  \"- 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.\",\n  \"- 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.\",\n  \"- 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.\",\n  \"- Use child issues for parallel or long delegated work instead of polling agents, sessions, or processes.\",\n  \"- If woken by a human comment on a dependency-blocked issue, respond or triage the comment without treating the blocked deliverable work as unblocked.\",\n  \"- 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.\",\n  \"- 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.\",\n  \"- 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.\",","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/adapter-utils/src/server-utils.ts#L136-L172","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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 '.'."],"exampleFix":"// before\nPAPERCLIP_INSTANCE_ID=acme.io/prod\n// after\nPAPERCLIP_INSTANCE_ID=acme-prod","handlingStrategy":"validation","validationCode":"const INSTANCE_ID_RE = /^[A-Za-z0-9_-]+$/;\nfunction resolveInstanceId(raw: string | undefined, env: NodeJS.ProcessEnv): string {\n  const id = (raw ?? env.PAPERCLIP_INSTANCE_ID ?? '').trim() || 'default';\n  if (!INSTANCE_ID_RE.test(id)) throw new Error(`Invalid instance id: '${id}'`);\n  return id;\n}","typeGuard":"function isValidInstanceId(id: string): boolean {\n  return typeof id === 'string' && /^[A-Za-z0-9_-]+$/.test(id);\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["config","environment","path-segment","validation","instance"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}