paperclipai/paperclip · error

CreateOS requires a shape from its shape catalog.

Error message

CreateOS requires a shape from its shape catalog.

What it means

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.

Solutions

  1. Add a valid 'shape' value to the CreateOS environment config, choosing one from the CreateOS shape catalog.
  2. If unsure which shapes exist, query the CreateOS account/catalog endpoint and copy the exact shape identifier.
  3. Check for typos or renames of the key — it must literally be 'shape'.

Example fix

// before
{ "apiUrl": "https://api.sb.createos.sh" }
// after
{ "apiUrl": "https://api.sb.createos.sh", "shape": "micro-2gb" }
Defensive patterns

Strategy: validation

Validate before calling

if (typeof cfg.shape !== 'string' || !cfg.shape.trim()) throw new Error('Set shape from the CreateOS shape catalog before creating the environment');

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/d01f138a5e9eb1db. Report an issue: GitHub.

Appendix: source

Thrown at packages/plugins/sandbox-providers/createos/src/config.ts:33

    if (typeof value !== "string" || !value.trim() || value.includes("\0")) {
      throw new Error(`${key} must be a non-empty string.`);
    }
    return value.trim();
  };
  const apiUrl = text("apiUrl");
  if (!apiUrl) throw new Error("CreateOS requires an API URL.");
  let url: URL;
  try { url = new URL(apiUrl); } catch { throw new Error("CreateOS API URL is invalid."); }
  // Configuration is board-owned, but never follow redirects with the API key.
  // Plain HTTP is useful for a loopback development server only.
  const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
  if ((url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) ||
      url.username || url.password || url.search || url.hash ||
      !["", "/", "/v1", "/v1/"].includes(url.pathname)) {
    throw new Error("CreateOS API URL must be an HTTPS origin (optionally ending in /v1); HTTP is allowed on loopback only.");
  }
  const shape = text("shape");
  if (!shape) throw new Error("CreateOS requires a shape from its shape catalog.");
  const timeoutMs = raw.timeoutMs ?? 300_000;
  if (typeof timeoutMs !== "number" || !Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 86_400_000) {
    throw new Error("timeoutMs must be an integer between 1 and 86400000.");
  }
  if (raw.reuseLease != null && typeof raw.reuseLease !== "boolean") {
    throw new Error("reuseLease must be a boolean.");
  }
  return {
    apiUrl: url.origin,
    apiKey: text("apiKey"),
    shape,
    rootfs: text("rootfs"),
    region: text("region"),
    timeoutMs,
    reuseLease: raw.reuseLease === true,
  };
}

View on GitHub (pinned to 3f1d897a7c)