paperclipai/paperclip · error

ACPX_SESSION_ENSURE_NON_ERROR

ACPX_SESSION_ENSURE_NON_ERROR

Error message

ACPX session ensure rejected a non-error

What it means

classifySessionEnsureFailure converts whatever the ACPX session-ensure operation threw into a typed error. If the rejected value is not an Error instance, it cannot be classified as TYPE_ERROR or FAILED, so this fallback Error with code ACPX_SESSION_ENSURE_NON_ERROR is produced. It indicates the underlying runtime rejected the promise with a non-Error value (string, object, undefined).

Source

Thrown at packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts:469

  }
}

/** Backward-compatible name retained for existing Codex-only consumers. */
export const openCodexAcpxRuntime = openQualifiedAcpxRuntime;

function classifySessionEnsureFailure(error: unknown): Error {
  if (error instanceof Error) {
    const details = error as Error & Record<string, unknown>;
    if (typeof details.code !== "string" || details.code.length === 0) {
      details.code =
        error instanceof TypeError
          ? "ACPX_SESSION_ENSURE_TYPE_ERROR"
          : "ACPX_SESSION_ENSURE_FAILED";
    }
    return error;
  }
  return Object.assign(new Error("ACPX session ensure rejected a non-error"), {
    code: "ACPX_SESSION_ENSURE_NON_ERROR",
  });
}

function raceRuntimeHandshakeWithAbort<T>(
  handshake: Promise<T>,
  signal: AbortSignal,
): Promise<T> {
  signal.throwIfAborted();
  return new Promise<T>((resolve, reject) => {
    let settled = false;
    const settle = (operation: () => void): void => {
      if (settled) return;
      settled = true;
      signal.removeEventListener("abort", onAbort);
      operation();
    };
    const onAbort = (): void => settle(() => reject(signal.reason));
    signal.addEventListener("abort", onAbort, { once: true });

View on GitHub (pinned to 01ad858492)

Solutions

  1. Inspect the runtime logs to find the original rejection value; the non-Error value usually carries the real cause.
  2. Coerce rejections to Errors at the boundary (Promise catch mapping or util.toError) in the ACPX host.
  3. Pin/upgrade the ACPX runtime to a version that rejects with proper Error objects.
  4. Check that the host process/child runtime did not crash mid-handshake; fix the crash before the classification layer matters.

Example fix

// before
reject({ reason: "spawn failed" }); // runtime rejects non-Error
// after
reject(new Error("spawn failed")); // classifySessionEnsureFailure can classify it
Defensive patterns

Strategy: try-catch

Type guard

function isError(v) {
  return v instanceof Error;
}

Try / catch

try {
  await driver.handshake();
} catch (err) {
  if (err.code === "ACPX_SESSION_ENSURE_NON_ERROR") {
    // log err.stack and inspect raw runtime logs for the original rejection value
  } else throw err;
}

Prevention

When it happens

Trigger: The ACPX handshake/session-ensure promise rejects with a string, plain object, or undefined instead of an Error, e.g. a runtime bug, a cross-realm throw, or a host process crashing and rejecting with a serialized payload.

Common situations: Mixed Node versions or bundled runtimes throwing non-Error values; IPC layer rejecting with raw payload objects; third-party ACPX runtime updates changing rejection values.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/eae5d21f22348e14. Report an issue: GitHub.