paperclipai/paperclip · error

ACPX runtime omitted acpxRecordId

Error message

ACPX runtime omitted acpxRecordId

What it means

requireIdentity() validates that an ACP runtime handle carries a non-empty acpxRecordId before the adapter will open runtime ports. The acpxRecordId is the persistent identifier of the ACPX runtime record in the control plane; without it the adapter cannot bind or verify which runtime record a session belongs to, so it throws instead of proceeding with a corrupt/anonymous handle.

Source

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

      status,
      tokenBudget:
        goal.tokenBudget === null ? null : optionalNumber(goal.tokenBudget),
      tokensUsed: optionalNumber(goal.tokensUsed),
      timeUsedSeconds: optionalNumber(goal.timeUsedSeconds),
      iterations: optionalNumber(goal.iterations),
      lastReason:
        goal.lastReason === null || typeof goal.lastReason === "string"
          ? goal.lastReason
          : undefined,
      createdAt: optionalTimestamp(goal.createdAt),
      updatedAt: optionalTimestamp(goal.updatedAt),
    },
  };
}

function requireIdentity(handle: AcpRuntimeHandle): AcpxRuntimePortIdentity {
  const acpxRecordId = nonEmptyRuntimeIdentity(handle.acpxRecordId);
  if (!acpxRecordId) throw new Error("ACPX runtime omitted acpxRecordId");
  const backendSessionId = nonEmptyRuntimeIdentity(handle.backendSessionId);
  if (!backendSessionId) {
    throw new Error("ACPX runtime omitted backendSessionId");
  }
  return {
    acpxRecordId,
    backendSessionId,
    // ACPX agents do not all advertise a distinct native thread identity.
    // In that case the backend ID is the real ACP protocol session, so retain
    // it explicitly rather than inventing a Paperclip-owned identifier.
    agentSessionId:
      nonEmptyRuntimeIdentity(handle.agentSessionId) ?? backendSessionId,
  };
}

function definedEnvironment(
  environment: Readonly<NodeJS.ProcessEnv>,
): Record<string, string> {

View on GitHub (pinned to 01ad858492)

Solutions

  1. Ensure the runtime bootstrap path persists the ACPX runtime record and copies its id into handle.acpxRecordId before opening ports.
  2. Validate the handle shape right after obtaining it (assert typeof handle.acpxRecordId === 'string' && handle.acpxRecordId.trim() !== '').
  3. Upgrade/check the ACPX runtime version so it reports acpxRecordId in its bootstrap response.
  4. If the handle came from persistence, confirm the record row exists and was not deleted mid-session.

Example fix

// before: handle built without the record id
const handle = { backendSessionId: session.id } as AcpRuntimeHandle;
const identity = requireIdentity(handle);

// after
const handle = { acpxRecordId: runtimeRecord.id, backendSessionId: session.id } as AcpRuntimeHandle;
const identity = requireIdentity(handle);
Defensive patterns

Strategy: validation

Validate before calling

function hasAcpxRecordId(h) {
  return typeof h?.acpxRecordId === 'string' && h.acpxRecordId.trim().length > 0;
}
if (!hasAcpxRecordId(handle)) throw new Error('handle missing acpxRecordId before port setup');

Type guard

function hasAcpxRecordId(h: AcpRuntimeHandle): h is AcpRuntimeHandle & { acpxRecordId: string } {
  return typeof h.acpxRecordId === 'string' && h.acpxRecordId.trim() !== '';
}

Try / catch

try {
  const identity = requireIdentity(handle);
  openPorts(identity);
} catch (err) {
  if (err.message === 'ACPX runtime omitted acpxRecordId') {
    // re-bootstrap the runtime record and rebuild the handle
    handle = await rebuildRuntimeHandle();
  } else throw err;
}

Prevention

When it happens

Trigger: Passing an AcpRuntimeHandle whose acpxRecordId is undefined, null, empty string, or whitespace-only into requireIdentity() — typically when a handle was constructed from an incomplete spawn/bootstrap result, or a deserialized handle lost the field.

Common situations: A runtime record failed to persist before the handle was created (DB write failure upstream); an older ACPX runtime version that did not return acpxRecordId; code that manually constructs a handle object and forgets the field; JSON round-trip dropping undefined fields.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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