paperclipai/paperclip · error

CreateOS returned an invalid process frame.

Error message

CreateOS returned an invalid process frame.

What it means

A non-empty line from the CreateOS process stream failed JSON.parse or did not produce an object (via the `object()` helper). The plugin expects every frame to be a JSON object event (heartbeat/data/exit/error); anything else is treated as a protocol violation and fails closed rather than being reconnected past.

Solutions

  1. Log/capture the offending line and compare it against the expected event schema (type: heartbeat|data|exit|error).
  2. Check for proxies or gateways between the plugin and CreateOS that could inject non-JSON bodies into the stream.
  3. Verify CreateOS service version compatibility; protocol changes can break frame parsing.
  4. Retry the command; if persistent, inspect the sandbox's process endpoint directly with curl to see the raw frames.

Example fix

// before
const ev = JSON.parse(line);          // throws raw SyntaxError on bad frames
// after (plugin behavior)
try { const ev = object(JSON.parse(line)); } catch { /* invalid process frame */ }
Defensive patterns

Strategy: try-catch

Try / catch

try { await execute(client, params, signal); }
catch (e) {
  if (e instanceof Error && e.message === "CreateOS returned an invalid process frame.") {
    log.error("Non-JSON frame from CreateOS; check proxies and service version");
    // retry once, then fail with diagnostics
  } else throw e;
}

Prevention

When it happens

Trigger: The /processes/:id/connect stream emits a line that is invalid JSON (truncated frame, HTML error page, plain-text message), or valid JSON that is not an object (array, string, number).

Common situations: A proxy/load balancer injects an HTML 502 page into the stream; the sandbox command echoes non-JSON directly into the event channel due to a server-side framing bug; a CreateOS protocol version change renames or restructures event payloads.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at packages/plugins/sandbox-providers/createos/src/execute.ts:62

        pending = pending.slice(newline + 1);
        if (line.length > MAX_LINE_BYTES) throw new Error("CreateOS process frame is too large.");
        if (line.trim()) yield parseEvent(line);
      }
      if (pending.length > MAX_LINE_BYTES) throw new Error("CreateOS process frame is too large.");
      if (done) {
        if (pending.trim()) yield parseEvent(pending);
        return;
      }
    }
  } finally {
    await reader.cancel().catch(() => undefined);
    reader.releaseLock();
  }
}

function parseEvent(line: string): Record<string, unknown> {
  try { return object(JSON.parse(line)); }
  catch { throw new Error("CreateOS returned an invalid process frame."); }
}

class Output {
  stdout = "";
  stderr = "";
  truncated = false;
  private decoders = { stdout: new StringDecoder("utf8"), stderr: new StringDecoder("utf8") };
  constructor(private log: (stream: "stdout" | "stderr", text: string) => void) {}

  private append(stream: "stdout" | "stderr", text: string) {
    if (!text) return;
    this.log(stream, text);
    const combined = this[stream] + text;
    if (combined.length > MAX_CAPTURE_CHARS) this.truncated = true;
    this[stream] = combined.slice(-MAX_CAPTURE_CHARS);
  }

  write(stream: "stdout" | "stderr", bytes: Buffer) {

View on GitHub (pinned to 3f1d897a7c)