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
- Ensure the duplex gateway command has at least one element before opening the channel (the node binary path plus script path).
- Check the command template/interpolation that produced params.command for undefined or empty segments.
- 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
- Validate the command template at provider-config load time, not at channel-open time.
- Fail config load when an interpolation yields an empty binary path.
- Cover this with a unit test on the command builder using a populated argv.
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
- Daytona duplex channel: no cached sandbox resolves the provi
- Sandbox duplex channel needs a plugin id, a provider key, an
- 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@a7e689b3c3 (2026-08-21).
Data as JSON: /api/errors/70621a9b596d7bfa.
Report an issue: GitHub.