paperclipai/paperclip · error · AggregateError
ACPX command leases did not close
Error message
ACPX command leases did not close
What it means
Aggregate close failure in the verified ACPX command-lease owner: close() attempts to close every acquired lease, collects per-lease failures, and throws this aggregated error when any lease close failed (late acquisitions after closing starts remain owned and retried, but ones that still fail are surfaced). It signals that not all underlying command leases were released cleanly and some resource handle may still be open.
Solutions
- Read the AggregateError's inner failures to identify which lease(s) failed to close.
- Retry close after the underlying command/transport settles — the owner already retries each failing lease once during close.
- Investigate why a lease's close hung or errored (spawned process still running, transport stuck) and ensure leases are only acquired while the owner is open.
Defensive patterns
Strategy: retry
When it happens
Trigger: Thrown at packages/paperclip-runner/src/drivers/acpx/command-lease-owner.ts:32 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/758f31a2cbd5d932.
Report an issue: GitHub.
Appendix: source
Thrown at packages/paperclip-runner/src/drivers/acpx/command-lease-owner.ts:32
spawn(...args) {
if (closing) throw new Error("Verified ACPX command owner is closing");
consumed = true;
return current.spawn(...args);
},
async close() {
closing = true;
// Late acquisitions remain owned. Retry every lease whose close fails.
await refresh?.catch(() => undefined);
const failures: unknown[] = [];
for (const lease of leases) {
try {
await lease.close();
leases.delete(lease);
} catch (error) {
failures.push(error);
}
}
if (failures.length) throw new AggregateError(failures, "ACPX command leases did not close");
},
};
return {
command,
async refreshConsumedCommand(): Promise<void> {
if (closing) throw new Error("Verified ACPX command owner is closing");
if (!consumed) return;
if (!refresh) {
refresh = Promise.resolve()
.then(openCommand)
.then((replacement) => {
leases.add(replacement);
if (closing) throw new Error("Verified ACPX command owner closed during refresh");
current = replacement;
consumed = false;
});
}
const pending = refresh;View on GitHub (pinned to 3f1d897a7c)