paperclipai/paperclip · error
Sandbox duplex channel needs a plugin id, a provider key, an
Error message
Sandbox duplex channel needs a plugin id, a provider key, and a provider lease id on the lease.
What it means
Thrown by the plugin sandbox driver's openDuplexChannel when the lease metadata is incomplete: it needs readString(lease.metadata.pluginId), readString(lease.metadata.provider), and lease.providerLeaseId to address the correct plugin worker and provider session. Any of these missing or non-string produces this fixed error before any worker call is made (server/src/services/environment-runtime.ts:2487).
Source
Thrown at server/src/services/environment-runtime.ts:2487
},
async syncOut(input) {
return await callPluginEnvironmentSync("environmentSyncOut", input);
},
async openDuplexChannel(input) {
// Plugin-backed sandbox providers only: open the host-owned duplex route on
// the plugin worker. The lease scope mirrors the sandbox execute path — the
// provider driver key, the company, the environment, and the provider lease
// id — so the route binds to the same worker session the runner streams.
if (!input.lease.metadata?.sandboxProviderPlugin || !pluginWorkerManager) {
throw new Error("Sandbox driver does not support duplex channels for this lease.");
}
const pluginId = readString(input.lease.metadata?.pluginId);
const providerKey = readString(input.lease.metadata?.provider);
const providerLeaseId = readString(input.lease.providerLeaseId);
if (!pluginId || !providerKey || !providerLeaseId) {
throw new Error(
"Sandbox duplex channel needs a plugin id, a provider key, and a provider lease id on the lease.",
);
}
const worker = pluginWorkerManager.getWorker(pluginId);
if (!worker) {
throw new Error(`Plugin worker "${pluginId}" is not running for the duplex channel.`);
}
const managerInput: WorkerManagerDuplexChannelOpenInput = {
driverKey: providerKey,
companyId: input.lease.companyId,
environmentId: input.environment.id,
providerLeaseId,
command: input.command,
};
const session = await worker.openDuplexChannel(managerInput);
return adaptDuplexChannelHostSession(session);
},
View on GitHub (pinned to a7e689b3c3)
Solutions
- Inspect lease.metadata for pluginId and provider keys and lease.providerLeaseId — confirm all three are present strings.
- Re-acquire the sandbox lease from the plugin provider so the full metadata shape is written by the current driver version.
- Update the sandbox plugin and server together so metadata keys stay in sync.
- If providerLeaseId is the missing one, ensure the provider-side lease was granted before the channel open is attempted.
Example fix
// before
await driver.openDuplexChannel({ lease, environment, command }); // throws: needs plugin id, provider key, provider lease id
// after
const ok = typeof lease.metadata?.pluginId === "string"
&& typeof lease.metadata?.provider === "string"
&& typeof lease.providerLeaseId === "string";
if (!ok) lease = await acquireFreshPluginLease(environment);
await driver.openDuplexChannel({ lease, environment, command }); Defensive patterns
Strategy: validation
Validate before calling
const { pluginId, provider } = (lease.metadata ?? {}) as { pluginId?: unknown; provider?: unknown };
if (typeof pluginId !== "string" || typeof provider !== "string" || typeof lease.providerLeaseId !== "string") { lease = await acquireFreshPluginSandboxLease(environment); } Type guard
function hasDuplexLeaseIdentity(lease: { metadata?: Record<string, unknown> | null; providerLeaseId?: string | null }): boolean {
return typeof lease.metadata?.pluginId === "string"
&& typeof lease.metadata?.provider === "string"
&& typeof lease.providerLeaseId === "string";
} Try / catch
try { await openDuplex(input); } catch (err) { if (/needs a plugin id, a provider key/.test((err as Error).message)) { input.lease = await acquireFreshPluginSandboxLease(env); await openDuplex(input); } else throw err; } Prevention
- Treat pluginId/provider/providerLeaseId as a contract between server and plugin — keep versions aligned when either changes.
- Never persist leases across plugin upgrades without validating the metadata shape on restore.
- Unit-test the metadata shape at lease acquisition time.
When it happens
Trigger: openDuplexChannel on a lease that has sandboxProviderPlugin set but whose metadata lacks pluginId or provider, or whose providerLeaseId is unset — e.g. the provider driver stored metadata under different keys, the lease was deserialized from an older shape, or the provider lease handshake never completed.
Common situations: Plugin sandbox provider version mismatch (metadata keys renamed between plugin versions); a lease restored/persisted from an older release losing providerLeaseId; partially initialized lease where the provider grant step failed silently.
Related errors
- Sandbox driver does not support duplex channels for this lea
- ${prefix}: the capability must be an object.
- ${prefix}: "sandboxTransport" must be one of ${ADAPTER_LOGIN
- sandbox runtime asset key is not a simple path segment: ${ke
- sandbox runtime asset key collides with a reserved runtime a
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-21).
Data as JSON: /api/errors/2cc91d4b712d79f6.
Report an issue: GitHub.