paperclipai/paperclip · error

CreateOS does not yet support leases with a guaranteed…

Error message

CreateOS does not yet support leases with a guaranteed expiration deadline.

What it means

acquire() creates a CreateOS sandbox lease, but the provider cannot guarantee a hard expiration at a caller-requested time: sandboxes only enforce an idle timeout, and the plugin refuses to promise an expiry it cannot enforce. If the caller supplies requestedExpiresAt, acquire throws immediately instead of creating a lease that might outlive the deadline.

Solutions

  1. Call acquire without requestedExpiresAt and rely on the sandbox idle timeout / explicit lease release.
  2. Schedule your own release (plugin.release or delete sandbox) with a host-local timer if a soft deadline suffices.
  3. If a guaranteed expiry is a hard requirement, use a different sandbox provider that supports provider-side expiration deadlines.

Example fix

// before
await acquire({ companyId, environmentId, config, requestedExpiresAt: new Date(Date.now() + 3600_000) });
// after
await acquire({ companyId, environmentId, config }); // manage release yourself
Defensive patterns

Strategy: validation

Validate before calling

if (params.requestedExpiresAt != null) {
  throw new Error("createos leases cannot guarantee expiration; omit requestedExpiresAt");
}

Try / catch

try {
  lease = await acquire(params);
} catch (err) {
  if (err.message.includes("guaranteed expiration")) {
    lease = await acquire({ ...params, requestedExpiresAt: undefined });
  } else throw err;
}

Prevention

When it happens

Trigger: onEnvironmentProbe → acquire is called with params.requestedExpiresAt set (non-null), e.g. the orchestrator asks for a lease guaranteed to end at a specific timestamp.

Common situations: Budget or governance policies that require time-boxed leases; callers porting from providers that support deadline leases; scheduling code that always sets an expiry by default.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at packages/plugins/sandbox-providers/createos/src/plugin.ts:24

} from "@paperclipai/plugin-sdk";
import { CreateosApiError, CreateosClient, object } from "./client.js";
import { parseConfig, resolveApiKey } from "./config.js";
import { CreateosCleanupError, execute, shellQuote } from "./execute.js";
import { syncFiles } from "./file-sync.js";

const CWD = "/paperclip-workspace";
// The host excludes .paperclip-runtime from workspace export, so the lease
// marker never becomes a user repository file.
const MARKER = `${CWD}/.paperclip-runtime/.paperclip-createos-lease`;

function metadataMatches(params: PluginEnvironmentDriverBaseParams, metadata?: Record<string, unknown>): boolean {
  return metadata?.provider === "createos" && metadata.companyId === params.companyId &&
    metadata.environmentId === params.environmentId && metadata.apiUrl === parseConfig(params.config).apiUrl;
}

async function acquire(params: PluginEnvironmentAcquireLeaseParams): Promise<PluginEnvironmentLease> {
  // An idle timeout or a host-local timer cannot supply a provider expiry.
  if (params.requestedExpiresAt) throw new Error("CreateOS does not yet support leases with a guaranteed expiration deadline.");
  const config = parseConfig(params.config);
  const client = new CreateosClient(config);
  const signal = AbortSignal.timeout(config.timeoutMs);
  const sandbox = await client.createSandbox(signal);
  try {
    await client.transition(sandbox.id, "running", signal);
    const data = await client.json(`/sandboxes/${sandbox.id}/exec`, "POST", {
      cmd: "/bin/bash", args: ["-lc", `mkdir -p -- ${shellQuote(CWD)}`],
    }, signal);
    if (object(data.result).exit_code !== 0) throw new Error("CreateOS workspace preparation failed; the image must provide Bash.");
    const marker = randomUUID();
    await client.upload(sandbox.id, MARKER, marker, signal);
    return {
      providerLeaseId: sandbox.id,
      metadata: {
        provider: "createos", apiUrl: config.apiUrl,
        companyId: params.companyId, environmentId: params.environmentId,
        remoteCwd: CWD, shellCommand: "bash", marker,

View on GitHub (pinned to 3f1d897a7c)