paperclipai/paperclip · error

Environment driver "${driver.driver}" does not support duple

Error message

Environment driver "${driver.driver}" does not support duplex channels.

What it means

Thrown by the environment runtime's openDuplexChannel facade when the resolved driver for the lease does not implement openDuplexChannel (server/src/services/environment-runtime.ts:3474). Driver support is optional: only the plugin-backed sandbox driver provides duplex channels today; drivers like the local process driver expose syncOut/execute only. The check mirrors the native file sync guard above it ("does not support native file sync").

Source

Thrown at server/src/services/environment-runtime.ts:3474

        throw new Error(`Environment driver "${driver.driver}" does not support native file sync.`);
      }
      return await driver.syncIn(input);
    },

    async syncOut(input: EnvironmentDriverSyncInput): Promise<PluginEnvironmentSyncResult> {
      const driver = requireDriverKey(getLeaseDriverKey(input.lease, input.environment));
      if (!driver.syncOut) {
        throw new Error(`Environment driver "${driver.driver}" does not support native file sync.`);
      }
      return await driver.syncOut(input);
    },

    async openDuplexChannel(
      input: EnvironmentDriverOpenDuplexChannelInput,
    ): Promise<CommandManagedDuplexChannel> {
      const driver = requireDriverKey(getLeaseDriverKey(input.lease, input.environment));
      if (!driver.openDuplexChannel) {
        throw new Error(`Environment driver "${driver.driver}" does not support duplex channels.`);
      }
      // Centralize the duplex channel authorization here. Resolve the exact lease
      // capability snapshot and refuse unless the effective snapshot grants the
      // opt-in `duplexCommandStream` capability. This gate runs before the driver
      // call, so an unauthorized lease never reaches the worker. The
      // execution-target member gate stays as defense in depth. A driver that
      // cannot resolve the snapshot fails closed with the fixed refusal.
      const effective =
        (await driver.effectiveSandboxCapabilities?.(input)) ?? null;
      if (effective?.duplexCommandStream !== true) {
        throw new Error(DUPLEX_CHANNEL_CAPABILITY_DENIED);
      }
      return await driver.openDuplexChannel(input);
    },
  };
}

export type EnvironmentRuntimeService = ReturnType<typeof environmentRuntimeService>;

View on GitHub (pinned to a7e689b3c3)

Solutions

  1. Feature-detect before calling: check typeof driver.openDuplexChannel === "function" or gate on the lease being a plugin sandbox lease.
  2. Use the driver's supported execution path (execute) when duplex is unavailable.
  3. Provision the environment with the plugin sandbox provider driver if duplex streaming is required.
  4. For custom drivers, implement openDuplexChannel (and effectiveSandboxCapabilities) to opt into the feature.

Example fix

// before
const ch = await envRuntime.openDuplexChannel({ lease, environment, command }); // throws for local driver

// after
const duplexSupported = typeof getDriver(getLeaseDriverKey(lease, environment))?.openDuplexChannel === "function";
if (!duplexSupported) {
  await envRuntime.execute({ lease, environment, command });
} else {
  const ch = await envRuntime.openDuplexChannel({ lease, environment, command });
}
Defensive patterns

Strategy: type-guard

Validate before calling

const driverKey = getLeaseDriverKey(lease, environment);
const driver = requireDriverKey(driverKey);
if (typeof driver.openDuplexChannel !== "function") { return await runtime.execute({ lease, environment, command }); }

Type guard

function driverSupportsDuplex(driver: { openDuplexChannel?: unknown }): boolean {
  return typeof driver.openDuplexChannel === "function";
}

Try / catch

try { return await runtime.openDuplexChannel(input); } catch (err) { if (/does not support duplex channels/.test((err as Error).message)) { return await runtime.execute(input); /* degrade to one-shot */ } throw err; }

Prevention

When it happens

Trigger: Calling openDuplexChannel on the runtime facade for a lease whose driver key resolves to a driver without an openDuplexChannel method — e.g. a local/docker environment lease, or a plugin sandbox driver instantiated without a pluginWorkerManager.

Common situations: Code written against plugin sandboxes run against local dev environments; a custom driver registered without duplex support; attempting interactive streaming against a driver that only supports one-shot execute.

Related errors


AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-21). Data as JSON: /api/errors/fbc48e17530ae3db. Report an issue: GitHub.