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
- Inspect the runtime logs to find the original rejection value; the non-Error value usually carries the real cause.
- Coerce rejections to Errors at the boundary (Promise catch mapping or util.toError) in the ACPX host.
- Pin/upgrade the ACPX runtime to a version that rejects with proper Error objects.
- 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
- Always reject promises with Error instances in the ACPX host boundary.
- Pin and test against a known ACPX runtime version.
- Log unclassified rejection values at the source for easier diagnosis.
- Handle child-process crashes during handshake explicitly.
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
- run.result.proposed
- persisted Codex ACPX session identity is inconsistent
- persisted Codex ACPX resultless recovery requires a complete
- ACPX provider identity is incomplete
- DUPLEX_CHANNEL_OPEN_FAILED
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/eae5d21f22348e14.
Report an issue: GitHub.