{"record":{"id":"76cd561cc94f654d","repo":"paperclipai/paperclip","slug":"sandbox-driver-does-not-support-duplex-channels-fo","errorCode":null,"errorMessage":"Sandbox driver does not support duplex channels for this lease.","messagePattern":"Sandbox driver does not support duplex channels for this lease\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/environment-runtime.ts","lineNumber":2481,"sourceCode":"      }\n      return true;\n    },\n\n    async syncIn(input) {\n      return await callPluginEnvironmentSync(\"environmentSyncIn\", input);\n    },\n\n    async syncOut(input) {\n      return await callPluginEnvironmentSync(\"environmentSyncOut\", input);\n    },\n\n    async openDuplexChannel(input) {\n      // Plugin-backed sandbox providers only: open the host-owned duplex route on\n      // the plugin worker. The lease scope mirrors the sandbox execute path — the\n      // provider driver key, the company, the environment, and the provider lease\n      // id — so the route binds to the same worker session the runner streams.\n      if (!input.lease.metadata?.sandboxProviderPlugin || !pluginWorkerManager) {\n        throw new Error(\"Sandbox driver does not support duplex channels for this lease.\");\n      }\n      const pluginId = readString(input.lease.metadata?.pluginId);\n      const providerKey = readString(input.lease.metadata?.provider);\n      const providerLeaseId = readString(input.lease.providerLeaseId);\n      if (!pluginId || !providerKey || !providerLeaseId) {\n        throw new Error(\n          \"Sandbox duplex channel needs a plugin id, a provider key, and a provider lease id on the lease.\",\n        );\n      }\n      const worker = pluginWorkerManager.getWorker(pluginId);\n      if (!worker) {\n        throw new Error(`Plugin worker \"${pluginId}\" is not running for the duplex channel.`);\n      }\n      const managerInput: WorkerManagerDuplexChannelOpenInput = {\n        driverKey: providerKey,\n        companyId: input.lease.companyId,\n        environmentId: input.environment.id,\n        providerLeaseId,","sourceCodeStart":2463,"sourceCodeEnd":2499,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/server/src/services/environment-runtime.ts#L2463-L2499","documentation":"Thrown by the plugin-backed sandbox environment driver's openDuplexChannel when the lease was not created by a plugin sandbox provider or the server has no plugin worker manager. Duplex channels (bidirectional command streaming into a sandbox) are only implemented as a host-owned route on a plugin worker; the guard at server/src/services/environment-runtime.ts:2481 requires lease.metadata.sandboxProviderPlugin to be set and a pluginWorkerManager to exist, otherwise the operation is unsupported for that lease.","triggerScenarios":"Calling openDuplexChannel on the environment runtime with a lease whose metadata lacks sandboxProviderPlugin (a local/docker/inline lease routed to the plugin adapter), or when the server was constructed without a pluginWorkerManager (plugin subsystem disabled) even though the lease metadata claims a plugin provider.","commonSituations":"Agent tooling attempts to stream commands into a sandbox that was provisioned by a non-plugin driver; the feature flag/config enabling plugin workers is off in that deployment; the lease was created before the plugin sandbox provider existed (old metadata shape).","solutions":["Verify the lease is a plugin-provider sandbox lease: its metadata must include sandboxProviderPlugin (set at provisioning time by the plugin sandbox driver).","Ensure the server runtime was initialized with a pluginWorkerManager and the sandbox plugin is enabled in server config.","Provision the environment through the plugin sandbox provider so leases carry the required metadata before requesting duplex channels.","If you only need one-shot command execution, use the regular sandbox execute path which does not require a duplex channel."],"exampleFix":"// before\nconst channel = await runtime.openDuplexChannel({ lease, environment, command }); // throws: driver does not support duplex channels\n\n// after\nif (!lease.metadata?.sandboxProviderPlugin) {\n  const result = await runtime.execute({ lease, environment, command }); // one-shot path\n} else {\n  const channel = await runtime.openDuplexChannel({ lease, environment, command });\n}","handlingStrategy":"type-guard","validationCode":"const isPluginLease = Boolean(lease.metadata?.sandboxProviderPlugin);\nconst pluginWorkersEnabled = Boolean(serverFeatures.pluginWorkerManager);\nif (!isPluginLease || !pluginWorkersEnabled) { await runtime.execute({ lease, environment, command }); } // one-shot fallback path","typeGuard":"function supportsPluginDuplexChannel(lease: { metadata?: Record<string, unknown> | null }): boolean {\n  return lease.metadata?.sandboxProviderPlugin === true || typeof lease.metadata?.sandboxProviderPlugin === \"string\";\n}","tryCatchPattern":"try { return await driver.openDuplexChannel(input); } catch (err) { if (/does not support duplex channels/.test(String((err as Error).message))) { return await runtime.execute(input); } throw err; }","preventionTips":["Only request duplex channels on leases provisioned by the plugin sandbox provider.","Deploy with the plugin worker manager enabled when duplex streaming is part of the workflow.","Gate duplex-based tooling on the lease metadata, not on best-effort attempts."],"tags":["sandbox","duplex-channel","plugin","lease","unsupported-operation"],"backgroundTag":"unsupported-driver-operation","analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-21T17:58:32.592Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}