{"record":{"id":"68ec937eec36ef64","repo":"paperclipai/paperclip","slug":"createos-connection-failed","errorCode":null,"errorMessage":"CreateOS connection failed.","messagePattern":"CreateOS connection failed\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/plugins/sandbox-providers/createos/src/client.ts","lineNumber":54,"sourceCode":"    this.apiKey = resolveApiKey(config);\n  }\n\n  async request(path: string, init: RequestInit = {}): Promise<Response> {\n    const signal = init.signal ?? AbortSignal.timeout(this.config.timeoutMs);\n    await waitForRequest(this.config.apiUrl, signal);\n    let response: Response;\n    try {\n      response = await fetch(`${this.config.apiUrl}/v1${path}`, {\n        ...init,\n        redirect: \"error\",\n        headers: { ...init.headers, \"X-Api-Key\": this.apiKey },\n        signal,\n      });\n    } catch (error) {\n      if (init.signal?.aborted) throw init.signal.reason;\n      // Do not propagate fetch causes: they can contain the configured URL.\n      if (error instanceof Error && error.name === \"TimeoutError\") throw error;\n      throw new Error(\"CreateOS connection failed.\");\n    }\n    if (!response.ok) {\n      await response.body?.cancel();\n      // Only fixed operation labels: never include paths, queries, or bodies,\n      // which may contain credentials or private workspace names.\n      const operation = path.includes(\"/files?\") ? \"file transfer\"\n        : path.includes(\"/stdin/close\") ? \"stdin close\"\n        : path.includes(\"/connect?\") ? \"output connection\"\n        : path.endsWith(\"/processes\") ? \"process creation\"\n        : path.includes(\"/processes/\") ? \"process cleanup\"\n        : path.endsWith(\"/exec\") ? \"workspace command\"\n        : \"sandbox lifecycle\";\n      throw new CreateosApiError(response.status, operation);\n    }\n    return response;\n  }\n\n  async json(path: string, method = \"GET\", body?: unknown, signal?: AbortSignal): Promise<Record<string, unknown>> {","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/packages/plugins/sandbox-providers/createos/src/client.ts#L36-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: assuming the error message is all you get\ntry { await client.getSandbox(id); } catch { /* connection failed, unknown why */ }\n// after: retrying with backoff on connection failures\nasync function withRetry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {\n  for (let i = 1; ; i++) {\n    try { return await fn(); }\n    catch (e) {\n      if (i >= attempts || !(e instanceof Error) || e.message !== \"CreateOS connection failed.\") throw e;\n      await new Promise(r => setTimeout(r, 500 * 2 ** i));\n    }\n  }\n}\nawait withRetry(() => client.getSandbox(id));","handlingStrategy":"retry","validationCode":"// Reachability check before calling the API\nconst url = new URL(config.apiUrl);\nconst ok = await fetch(`${url.protocol}//${url.host}`, { method: \"HEAD\", signal: AbortSignal.timeout(5000) })\n  .then(() => true).catch(() => false);\nif (!ok) throw new Error(\"CreateOS host unreachable before request.\");","typeGuard":null,"tryCatchPattern":"try {\n  await client.getSandbox(id);\n} catch (e) {\n  if (e instanceof Error && e.message === \"CreateOS connection failed.\") {\n    // network-layer failure: retry with backoff or surface as infra incident\n  } else throw e;\n}","preventionTips":["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."],"tags":["network","fetch","connectivity","sandbox-provider"],"backgroundTag":"network-request-failed","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}