{"record":{"id":"d01f138a5e9eb1db","repo":"paperclipai/paperclip","slug":"createos-requires-a-shape-from-its-shape-catalog","errorCode":null,"errorMessage":"CreateOS requires a shape from its shape catalog.","messagePattern":"CreateOS requires a shape from its shape catalog\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/plugins/sandbox-providers/createos/src/config.ts","lineNumber":33,"sourceCode":"    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 {\n    apiUrl: url.origin,\n    apiKey: text(\"apiKey\"),\n    shape,\n    rootfs: text(\"rootfs\"),\n    region: text(\"region\"),\n    timeoutMs,\n    reuseLease: raw.reuseLease === true,\n  };\n}\n","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/packages/plugins/sandbox-providers/createos/src/config.ts#L15-L51","documentation":"parseConfig requires a non-empty 'shape' string identifying a VM shape from the CreateOS shape catalog. The plugin cannot request a sandbox environment without knowing which shape to provision, so a missing/empty shape is rejected at config-parse time rather than at request time.","triggerScenarios":"Calling parseConfig (or building the plugin client / onEnvironmentValidateConfig / onEnvironmentProbe) with environment config that omits the 'shape' key or sets it to an empty/whitespace-only string.","commonSituations":"Newly created environment config where the shape field was never filled in; copying a config template and deleting the placeholder; renaming the key (e.g. 'instanceType') by mistake.","solutions":["Add a valid 'shape' value to the CreateOS environment config, choosing one from the CreateOS shape catalog.","If unsure which shapes exist, query the CreateOS account/catalog endpoint and copy the exact shape identifier.","Check for typos or renames of the key — it must literally be 'shape'."],"exampleFix":"// before\n{ \"apiUrl\": \"https://api.sb.createos.sh\" }\n// after\n{ \"apiUrl\": \"https://api.sb.createos.sh\", \"shape\": \"micro-2gb\" }","handlingStrategy":"validation","validationCode":"if (typeof cfg.shape !== 'string' || !cfg.shape.trim()) throw new Error('Set shape from the CreateOS shape catalog before creating the environment');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Template CreateOS configs with a required, non-empty shape field","Validate shape identifiers against the catalog at authoring time","Never rename the 'shape' key in environment configs"],"tags":["config","validation","missing-field"],"backgroundTag":"missing-required-config-field","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}