paperclipai/paperclip · error
A reusable lease handoff requires an execution workspace and
Error message
A reusable lease handoff requires an execution workspace and provider lease id.
What it means
environmentService's lease lifecycle validation: when an operation hands off a reusable lease (replacesReusableLeaseId or reusesReusableLeaseId is set), the input must also carry executionWorkspaceId and providerLeaseId. It throws when a handoff/reacquisition is requested without the workspace and provider lease identifiers needed to bind the new lease.
Source
Thrown at server/src/services/environments.ts:1390
status: "active" as const,
leasePolicy: input.leasePolicy ?? "ephemeral",
provider: input.provider ?? null,
providerLeaseId: input.providerLeaseId ?? null,
acquiredAt: now,
lastUsedAt: now,
expiresAt: input.expiresAt ?? null,
releasedAt: null,
failureReason: null,
cleanupStatus: null,
metadata: input.metadata ?? null,
createdAt: now,
updatedAt: now,
};
if (
(input.replacesReusableLeaseId || input.reusesReusableLeaseId) &&
(!input.executionWorkspaceId || !input.providerLeaseId)
) {
throw new Error(
"A reusable lease handoff requires an execution workspace and provider lease id.",
);
}
if (input.reusesReusableLeaseId && !input.heartbeatRunId) {
throw new Error(
"A same-run reusable lease reacquisition requires a heartbeat run id.",
);
}
if (input.replacesReusableLeaseId && input.reusesReusableLeaseId) {
throw new Error(
"A reusable lease cannot be replaced and reacquired in the same operation.",
);
}
const row =
input.assertCompanyBinding ||
input.replacesReusableLeaseId ||
input.reusesReusableLeaseId
? await db.transaction(async (tx) => {View on GitHub (pinned to 01ad858492)
Solutions
- Populate both executionWorkspaceId and providerLeaseId whenever replacesReusableLeaseId or reusesReusableLeaseId is passed
- Fix the caller to only set the handoff flags when a real provider lease exists (check provider response before calling)
- If no handoff is intended, omit replacesReusableLeaseId/reusesReusableLeaseId entirely
- Backfill the missing workspace/provider lease ids from the provider API before retrying
Example fix
// before
acquireLease({ replacesReusableLeaseId: prevId })
// after
acquireLease({ replacesReusableLeaseId: prevId, executionWorkspaceId: ws.id, providerLeaseId: lease.providerLeaseId }) Defensive patterns
Strategy: validation
Validate before calling
if ((input.replacesReusableLeaseId || input.reusesReusableLeaseId) && (!input.executionWorkspaceId || !input.providerLeaseId)) {
throw new Error('handoff requires executionWorkspaceId and providerLeaseId');
}
await acquireLease(input); Type guard
function isCompleteLeaseHandoff(i: {replacesReusableLeaseId?:string;reusesReusableLeaseId?:string;executionWorkspaceId?:string|null;providerLeaseId?:string|null}): boolean {
return !(i.replacesReusableLeaseId || i.reusesReusableLeaseId) || Boolean(i.executionWorkspaceId && i.providerLeaseId);
} Try / catch
try {
await environmentService.acquireReusableLease(input);
} catch (e) {
if (e.message.includes('handoff requires')) {
const providerLease = await provider.createLease(); // fetch missing ids then retry
return environmentService.acquireReusableLease({ ...input, executionWorkspaceId: ws.id, providerLeaseId: providerLease.id });
}
throw e;
} Prevention
- Only set handoff flags after the provider successfully returned a lease id
- Validate lease inputs with a shared schema (zod) before calling environmentService
- Backfill executionWorkspaceId from the workspace record when absent
- Add tests covering every combination of replaces/reuses flags
When it happens
Trigger: Calling the lease acquire/replace path with replacesReusableLeaseId or reusesReusableLeaseId set but executionWorkspaceId or providerLeaseId null/undefined (e.g. seedReusablePluginSandboxLease or reusableLease callers omitting fields).
Common situations: Partial input construction after a refactor added the handoff fields; a caller migrating from non-reusable to reusable leases without provider lease ids; provider failed to return a lease id but the flow continued; DB row missing executionWorkspaceId.
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
- A same-run reusable lease reacquisition requires a heartbeat
- A reusable lease cannot be replaced and reacquired in the sa
- Invalid status '${String(rawStatus)}'. Must be one of: ${PLU
- "tool" is required and must be a string
- "runContext" is required and must be an object
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/c9104ef2fcfc1ae0.
Report an issue: GitHub.