paperclipai/paperclip · error
CreateOS connection failed.
Error message
CreateOS connection failed.
What it means
Thrown by `request()` when the underlying `fetch` to the CreateOS API rejects for a reason that is neither a caller abort nor a TimeoutError (e.g. DNS failure, connection refused/reset, TLS error). The client deliberately replaces the fetch cause with this fixed message because raw network errors can leak the configured apiUrl (and potentially credentials) into persisted errors and logs.
Solutions
- Verify network reachability: curl the CreateOS host from the same machine/container (`curl -v https://<api-url>/v1/...`).
- Check `config.apiUrl` for typos, wrong scheme (http vs https), wrong port, or trailing path mistakes.
- Confirm no caller-level AbortSignal was aborted before/during the request — those surface as the signal's reason instead, so seeing this message implies a genuine network-layer failure.
- Check egress/firewall/proxy configuration for the environment; set HTTPS_PROXY if required.
- Retry with backoff — transient connection resets and DNS blips are common; the client's pacer (`waitForRequest`) does not cover connection failures.
- Check CreateOS provider status/uptime for an outage.
Example fix
// before: assuming the error message is all you get
try { await client.getSandbox(id); } catch { /* connection failed, unknown why */ }
// after: retrying with backoff on connection failures
async function withRetry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {
for (let i = 1; ; i++) {
try { return await fn(); }
catch (e) {
if (i >= attempts || !(e instanceof Error) || e.message !== "CreateOS connection failed.") throw e;
await new Promise(r => setTimeout(r, 500 * 2 ** i));
}
}
}
await withRetry(() => client.getSandbox(id)); Defensive patterns
Strategy: retry
Validate before calling
// Reachability check before calling the API
const url = new URL(config.apiUrl);
const ok = await fetch(`${url.protocol}//${url.host}`, { method: "HEAD", signal: AbortSignal.timeout(5000) })
.then(() => true).catch(() => false);
if (!ok) throw new Error("CreateOS host unreachable before request."); Try / catch
try {
await client.getSandbox(id);
} catch (e) {
if (e instanceof Error && e.message === "CreateOS connection failed.") {
// network-layer failure: retry with backoff or surface as infra incident
} else throw e;
} Prevention
- Add a startup health probe against the CreateOS host before scheduling work.
- Set and monitor proxy/egress config (HTTPS_PROXY, security groups) in every environment.
- Distinguish aborts/timeouts (rethrown as-is) from this error when classifying failures.
- Alert on this error's rate — repeated occurrences mean outage, misconfig, or DNS problems, not per-request bugs.
When it happens
Trigger: Any client call (getSandbox, createSandbox, destroySandbox, transition, upload, json) where the TCP/TLS connection to `config.apiUrl` cannot be established or is reset mid-flight; wrong hostname/port, offline host, firewall, dead provider endpoint. Caller aborts (init.signal aborted) and `AbortSignal.timeout` expirations are rethrown as-is instead.
Common situations: Typo or stale value in the configured apiUrl; running in an environment without egress to the CreateOS host; provider outage or IP allowlist change; corporate proxy required but not configured; DNS resolution failure in containers.
Related errors
- GitHub webhook configuration could not be confirmed…
- network
- railway_request_failed
- Announcement request failed
- Anthropic Managed Agents request failed with HTTP
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/68ec937eec36ef64.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugins/sandbox-providers/createos/src/client.ts:54
this.apiKey = resolveApiKey(config);
}
async request(path: string, init: RequestInit = {}): Promise<Response> {
const signal = init.signal ?? AbortSignal.timeout(this.config.timeoutMs);
await waitForRequest(this.config.apiUrl, signal);
let response: Response;
try {
response = await fetch(`${this.config.apiUrl}/v1${path}`, {
...init,
redirect: "error",
headers: { ...init.headers, "X-Api-Key": this.apiKey },
signal,
});
} catch (error) {
if (init.signal?.aborted) throw init.signal.reason;
// Do not propagate fetch causes: they can contain the configured URL.
if (error instanceof Error && error.name === "TimeoutError") throw error;
throw new Error("CreateOS connection failed.");
}
if (!response.ok) {
await response.body?.cancel();
// Only fixed operation labels: never include paths, queries, or bodies,
// which may contain credentials or private workspace names.
const operation = path.includes("/files?") ? "file transfer"
: path.includes("/stdin/close") ? "stdin close"
: path.includes("/connect?") ? "output connection"
: path.endsWith("/processes") ? "process creation"
: path.includes("/processes/") ? "process cleanup"
: path.endsWith("/exec") ? "workspace command"
: "sandbox lifecycle";
throw new CreateosApiError(response.status, operation);
}
return response;
}
async json(path: string, method = "GET", body?: unknown, signal?: AbortSignal): Promise<Record<string, unknown>> {View on GitHub (pinned to 3f1d897a7c)