{"record":{"id":"e10b1002cf0c4644","repo":"paperclipai/paperclip","slug":"createos-api-url-is-invalid","errorCode":null,"errorMessage":"CreateOS API URL is invalid.","messagePattern":"CreateOS API URL is invalid\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/plugins/sandbox-providers/createos/src/config.ts","lineNumber":23,"sourceCode":"  rootfs: string | null;\n  region: string | null;\n  timeoutMs: number;\n  reuseLease: boolean;\n}\n\nexport function parseConfig(raw: Record<string, unknown>): CreateosConfig {\n  const text = (key: string): string | null => {\n    const value = raw[key];\n    if (value == null) return null;\n    if (typeof value !== \"string\" || !value.trim() || value.includes(\"\\0\")) {\n      throw new Error(`${key} must be a non-empty string.`);\n    }\n    return value.trim();\n  };\n  const apiUrl = text(\"apiUrl\");\n  if (!apiUrl) throw new Error(\"CreateOS requires an API URL.\");\n  let url: URL;\n  try { url = new URL(apiUrl); } catch { throw new Error(\"CreateOS API URL is invalid.\"); }\n  // Configuration is board-owned, but never follow redirects with the API key.\n  // Plain HTTP is useful for a loopback development server only.\n  const loopback = [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname);\n  if ((url.protocol !== \"https:\" && !(url.protocol === \"http:\" && loopback)) ||\n      url.username || url.password || url.search || url.hash ||\n      ![\"\", \"/\", \"/v1\", \"/v1/\"].includes(url.pathname)) {\n    throw new Error(\"CreateOS API URL must be an HTTPS origin (optionally ending in /v1); HTTP is allowed on loopback only.\");\n  }\n  const shape = text(\"shape\");\n  if (!shape) throw new Error(\"CreateOS requires a shape from its shape catalog.\");\n  const timeoutMs = raw.timeoutMs ?? 300_000;\n  if (typeof timeoutMs !== \"number\" || !Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 86_400_000) {\n    throw new Error(\"timeoutMs must be an integer between 1 and 86400000.\");\n  }\n  if (raw.reuseLease != null && typeof raw.reuseLease !== \"boolean\") {\n    throw new Error(\"reuseLease must be a boolean.\");\n  }\n  return {","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/packages/plugins/sandbox-providers/createos/src/config.ts#L5-L41","documentation":"After confirming apiUrl is present, parseConfig parses it with new URL() and rejects strings that are not absolute, well-formed URLs. This error means the configured apiUrl could not be parsed as a URL at all — it is not a syntax/protocol-policy rejection, which produces different messages.","triggerScenarios":"Configuring apiUrl as \"createos.example.com\" (missing scheme), \"https://\" (empty host), a value with spaces or invalid characters, or a relative path like \"/api\".","commonSituations":"Users omitting the https:// prefix when filling in the board config form, copy-paste introducing whitespace/quotes, or env files where the value got mangled (e.g. unescaped # truncating the URL).","solutions":["Change apiUrl to a full absolute URL including scheme, e.g. https://createos.example.com (http allowed only for localhost/127.0.0.1/[::1]).","Trim surrounding whitespace and remove stray quotes from the configured value.","Validate in the browser/node with `new URL(value)` before saving the config to fail fast with a friendly message.","If a path prefix is needed, keep it to \"\", \"/\", \"/v1\", or \"/v1/\" — other paths also fail validation."],"exampleFix":"// before\nparseConfig({ apiUrl: \"createos.internal\", apiKey });\n// after\nparseConfig({ apiUrl: \"https://createos.internal\", apiKey });","handlingStrategy":"validation","validationCode":"function prevalidateUrl(value) {\n  let url;\n  try { url = new URL(String(value).trim()); } catch { throw new Error(`apiUrl '${value}' is not a valid absolute URL; include the scheme (https://...)`); }\n  if (!url.protocol.startsWith(\"http\")) throw new Error(\"apiUrl must use http(s)\");\n  return url;\n}","typeGuard":null,"tryCatchPattern":"try {\n  config = parseConfig(raw);\n} catch (err) {\n  if (err.message === \"CreateOS API URL is invalid.\")\n    throw new Error(`apiUrl '${raw.apiUrl}' is not a valid URL. Use a full absolute URL like https://createos.example.com (path must be '', '/', '/v1', or '/v1/').`);\n  throw err;\n}","preventionTips":["Always include the scheme (https://) when entering apiUrl.","Trim whitespace and remove wrapping quotes from pasted URLs.","Validate with new URL() in the config UI before saving.","Keep the URL path to '', '/', '/v1', or '/v1/'; use http only for localhost/127.0.0.1/[::1]."],"tags":["config","url","validation"],"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"}