paperclipai/paperclip · error · Error

ACPX recovery identity does not match the persisted runtime

Error message

ACPX recovery identity does not match the persisted runtime record

What it means

verifyExpectedAcpxIdentity compares an expected session identity against the persisted runtime identity record (profile/workspace digests, permission mode, and provider lifetime fence candidates). Any mismatch means the runtime state on disk belongs to a different identity than the one being recovered, so it throws to prevent resuming a mismatched ACPX session. This protects against attaching a recovered session to the wrong agent configuration.

Source

Thrown at packages/paperclip-runner/src/drivers/acpx/recovery-identity.ts:190

  if (persisted === null) return;

  const record = parsePersistedRecord(persisted);
  if (
    record.acpxRecordId !== expected.acpxRecordId ||
    record.backendSessionId !== expected.backendSessionId ||
    record.agentSessionId !== expected.agentSessionId ||
    record.normalizedSessionId !== binding.normalizedSessionId ||
    record.profileDigest !== binding.profileDigest ||
    record.workspaceDigest !== binding.workspaceDigest ||
    record.requestedModel !== binding.requestedModel ||
    record.effectiveModel !== binding.effectiveModel ||
    record.permissionMode !== binding.permissionMode ||
    !sameFenceCandidates(
      record.providerLifetimeFenceCandidates,
      expected.providerLifetimeFenceCandidates,
    )
  ) {
    throw new Error(
      "ACPX recovery identity does not match the persisted runtime record",
    );
  }
}

function parsePersistedRecord(value: unknown): AcpxIdentityRecord {
  const record = object(value);
  rejectUnknownKeys(record, [
    "schema",
    "normalizedSessionId",
    "acpxRecordId",
    "backendSessionId",
    "agentSessionId",
    "profileDigest",
    "workspaceDigest",
    "requestedModel",
    "effectiveModel",
    "permissionMode",

View on GitHub (pinned to 01ad858492)

Solutions

  1. Start a fresh ACPX session instead of recovering — the persisted record belongs to a previous identity.
  2. Re-run with the exact same agent profile, workspace contents, and permissionMode used when the record was persisted.
  3. Delete or archive the stale runtime directory for that session id so a new identity record can be written.
  4. If this happens after a package upgrade, expect old sessions to be unrecoverable; do not force-match the record.

Example fix

// before
await acpx.open({ sessionId: oldId, permissionMode: "bypassAll" }); // record had "default"
// after
await acpx.open({ sessionId: oldId, permissionMode: "default" }); // match persisted record
// or: start a new session entirely
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await acpx.open({ sessionId, ...expected });
} catch (e) {
  if (e.message === "ACPX recovery identity does not match the persisted runtime record") {
    // fall back to a brand-new session; the old record is for a different identity
    await acpx.open({ sessionId: newSessionId(), ...currentConfig });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling open() / acpxProviderSessionIdentity() / createAcpxIdentityRecord() to recover an existing ACPX session where any of: record agent digest differs from binding digest, record.profileDigest differs, record.workspaceDigest differs, record.permissionMode !== binding.permissionMode, or the persisted fence candidates differ from expected.providerLifetimeFenceCandidates.

Common situations: Upgrading or downgrading the runner (profile digest changes) and then trying to resume an old session; editing the workspace between runs; launching with a different --permission-mode flag against a persisted session; reusing a runtime directory across sessions whose fence candidates were regenerated.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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