paperclipai/paperclip · error · Error

The duplex channel launch command needs at least one argumen

Error message

The duplex channel launch command needs at least one argument.

What it means

Thrown by buildDuplexChannelLaunchWrapper in the Daytona sandbox provider when the command array is empty. The wrapper builds an `exec <cmd>` shell line run on a raw PTY; an empty argv has nothing to exec, so it refuses to build the line instead of producing a shell that silently hangs the duplex channel.

Source

Thrown at packages/plugins/sandbox-providers/daytona/src/duplex-command-stream.ts:134

 *   1. `exec 2>'<diagnosticsPath>'` redirects the shell's stderr to the file. The
 *      later `exec` inherits it, so the gateway diagnostics land in the file and
 *      never on the stdout frame stream.
 *   2. `stty raw -echo` sets the terminal to raw mode with echo off, so the
 *      terminal neither echoes host input as data nor translates newlines.
 *   3. `exec '<program>' '<arg>'...` replaces the shell with the gateway, so the
 *      PTY runs the gateway directly and the gateway exit code becomes the PTY
 *      exit code.
 *
 * The wrapper quotes the diagnostics path and every command argument as a
 * single-quoted shell word. So a shell metacharacter in a path or an argument
 * stays literal text and cannot inject a shell command.
 */
export function buildDuplexChannelLaunchWrapper(
  command: readonly string[],
  diagnosticsPath: string,
): string {
  if (command.length === 0) {
    throw new Error("The duplex channel launch command needs at least one argument.");
  }
  const quotedCommand = command.map(shellQuote).join(" ");
  return (
    `exec 2>${shellQuote(diagnosticsPath)}; stty raw -echo; ` +
    `exec ${quotedCommand}${PTY_COMMAND_TERMINATOR}`
  );
}

/**
 * Opens a Daytona duplex channel PTY session for `command` and returns it as a
 * {@link DuplexChannelSession}. The session allocates a real pseudo-terminal in
 * raw mode, streams the raw output, accepts host input, and stops the child.
 *
 * The function decodes the terminal bytes as a UTF-8 stream, so a multibyte
 * character that splits across two output chunks stays whole. It buffers the
 * output until the transport registers the listener, so no early chunk is lost.
 */
export async function openDaytonaDuplexChannelSession(

View on GitHub (pinned to a7e689b3c3)

Solutions

  1. Ensure the duplex gateway command has at least one element before opening the channel (the node binary path plus script path).
  2. Check the command template/interpolation that produced params.command for undefined or empty segments.
  3. Validate command.length > 0 at the config boundary so the failure surfaces at configuration time, not channel-open time.

Example fix

// before
const line = buildDuplexChannelLaunchWrapper([], diagPath);

// after
if (command.length === 0) throw new Error("gateway command is empty; check provider config");
const line = buildDuplexChannelLaunchWrapper(command, diagPath);
Defensive patterns

Strategy: validation

Validate before calling

function isNonEmptyCommand(command: readonly string[]): boolean {
  return command.length > 0 && command.every((a) => typeof a === "string" && a.length > 0);
}

Type guard

function isLaunchableCommand(command: unknown): command is string[] {
  return Array.isArray(command) && command.length > 0 &&
    command.every((a) => typeof a === "string" && a.trim().length > 0);
}

Try / catch

try {
  wrapper = buildDuplexChannelLaunchWrapper(command, diagnosticsPath);
} catch (error) {
  if (error instanceof Error && error.message.includes("at least one argument")) {
    throw new Error("duplex gateway command is empty; fix the provider command template");
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling buildDuplexChannelLaunchWrapper([], diagnosticsPath) directly, or the provider's onDuplexChannelOpen receiving params.command = [] because the gateway command template rendered to zero arguments (empty config string, failed interpolation).

Common situations: Provider configuration where the command is assembled from optional pieces that are all unset; a template like `${prefix} ${rest}` with both empty; tests passing an empty array; a plugin version mismatch where the host sends an empty command for an unsupported gateway spec.

Related errors


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