paperclipai/paperclip · error
ACPX provider identity is incomplete
Error message
ACPX provider identity is incomplete
What it means
parseProviderIdentity rehydrates a persisted ACPX provider identity from stored/serialized state. It requires eight string fields — normalizedSessionId, acpxRecordId, backendSessionId, agentSessionId, profileDigest, workspaceDigest, requestedModel, effectiveModel — each a non-empty string of at most 240 characters. If any is missing, not a string, empty, or longer than 240 characters, it throws "ACPX provider identity is incomplete" rather than returning a partially populated identity.
Source
Thrown at packages/paperclip-runner/src/drivers/codex/codex-driver-values.ts:63
const requiredStrings = [
"normalizedSessionId",
"acpxRecordId",
"backendSessionId",
"agentSessionId",
"profileDigest",
"workspaceDigest",
"requestedModel",
"effectiveModel",
] as const;
if (
requiredStrings.some(
(key) =>
typeof identity[key] !== "string" ||
identity[key].length === 0 ||
identity[key].length > 240,
)
) {
throw new Error("ACPX provider identity is incomplete");
}
const permissionMode = identity.permissionMode;
if (
permissionMode !== undefined &&
permissionMode !== "approve-all" &&
permissionMode !== "approve-reads" &&
permissionMode !== "deny-all"
) {
throw new Error(
"ACPX provider identity contains an invalid permission mode",
);
}
const fenceCandidates = identity.providerLifetimeFenceCandidates;
if (
!Array.isArray(fenceCandidates) ||
fenceCandidates.length !== 3 ||
fenceCandidates.some(
(candidate) =>View on GitHub (pinned to 01ad858492)
Solutions
- Inspect the persisted provider identity object and populate every required key (normalizedSessionId, acpxRecordId, backendSessionId, agentSessionId, profileDigest, workspaceDigest, requestedModel, effectiveModel) with non-empty strings ≤240 chars.
- If the record predates a schema change, re-create the session/identity via the normal launch path instead of loading the stale record.
- Check the serialization path: ensure undefined/empty fields are not dropped silently by JSON.stringify or partial-update DB writes.
- Validate before persisting (same field checks at write time) so incomplete records can never be stored.
Example fix
// before
const identity = { kind: "acpx", normalizedSessionId: session.id, acpxRecordId: record.id };
parseProviderIdentity(identity); // throws
// after
const identity = {
kind: "acpx",
normalizedSessionId: session.id,
acpxRecordId: record.id,
backendSessionId: backend.id,
agentSessionId: agent.id,
profileDigest: profile.digest,
workspaceDigest: workspace.digest,
requestedModel: requested,
effectiveModel: effective,
};
parseProviderIdentity(identity); // ok Defensive patterns
Strategy: type-guard
Validate before calling
const REQUIRED_KEYS = ["normalizedSessionId","acpxRecordId","backendSessionId","agentSessionId","profileDigest","workspaceDigest","requestedModel","effectiveModel"] as const;
const complete = REQUIRED_KEYS.every((k) =>
typeof identity[k] === "string" && identity[k].length > 0 && identity[k].length <= 240
);
if (!complete) throw new Error("refusing to persist/load incomplete ACPX identity"); Type guard
function isCompleteApxIdentity(v: unknown): v is Record<string, string> {
if (typeof v !== "object" || v === null) return false;
const r = v as Record<string, unknown>;
if (r.kind !== "acpx") return false;
return (["normalizedSessionId","acpxRecordId","backendSessionId","agentSessionId","profileDigest","workspaceDigest","requestedModel","effectiveModel"] as const)
.every((k) => typeof r[k] === "string" && (r[k] as string).length > 0 && (r[k] as string).length <= 240);
} Try / catch
let identity: PersistedHarnessProviderIdentity | undefined;
try {
identity = parseProviderIdentity(stored);
} catch (error) {
if (error instanceof Error && error.message === "ACPX provider identity is incomplete") {
identity = undefined; // fall back to relaunching the session to rebuild identity
} else {
throw error;
}
} Prevention
- Validate identities at persist time with the same rules used at load time.
- Avoid storing records with undefined fields through JSON round-trips; use explicit nulls or reject at write.
- When adding required fields to the identity schema, write a migration/backfill for existing records.
- On parse failure, prefer re-creating the identity via a fresh launch over hand-patching stored records.
When it happens
Trigger: Calling parseProviderIdentity on an object with kind "acpx" where any required key is absent, null, a non-string (e.g. number), "", or >240 chars — e.g. parseProviderIdentity({ kind: "acpx", normalizedSessionId: "s", acpxRecordId: "r" }) (all other keys missing) or a stored record where backendSessionId was serialized as undefined.
Common situations: Schema/version drift where an older persisted record predates a newly required field; JSON round-trip dropping keys with undefined values; manual edits or partial writes to the persisted provider record; corruption of the digest fields by a failed profile/workspace digest computation that stored "".
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- persisted Codex ACPX session identity is inconsistent
- persisted Codex ACPX resultless recovery requires a complete
- run.result.proposed
- ACPX_SESSION_ENSURE_NON_ERROR
- ACPX_PERSISTED_SESSION_MISSING
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/1a119b5f768b209c.
Report an issue: GitHub.