can1357/oh-my-pi · error

ssh://: ${target.name} has no verified POSIX shell for ssh:/

Error message

ssh://: ${target.name} has no verified POSIX shell for ssh:// read/write — none of sh/bash/zsh round-tripped a capability probe (use `bash` with a remote SSH command for this host)

What it means

Even on a non-Windows remote, ssh:// transfers require a verified POSIX shell: the library probes sh, bash, and zsh by round-tripping a marker (`shell -lc echo marker`). If none verify, this error is thrown because quoting/reliable execution for head/cat/mv cannot be guaranteed.

Source

Thrown at packages/coding-agent/src/ssh/file-transfer.ts:37

 * wrap its snippet in `<shell> -c '…'`; OpenSSH otherwise hands the command
 * to the user's login shell, which on fish/csh/tcsh hosts can't parse our
 * `if [ … ]; then …` constructs (#3719).
 *
 * Windows hosts are refused up front — `ssh://` runs `head`/`cat`/`mv`/`test`
 * directly and cmd/powershell can't drive those. Everywhere else, we require
 * a non-empty `transferShell` (set by `probeHostInfo` after `sh -lc` /
 * `bash -lc` / `zsh -lc` round-trips a marker against the remote).
 */
async function ensurePosixRemote(target: SSHConnectionTarget): Promise<"sh" | "bash" | "zsh"> {
	await ensureConnection(target);
	const info = await ensureHostInfo(target);
	if (info.os === "windows") {
		throw new Error(
			`ssh://: ${target.name} is a Windows host; ssh:// supports POSIX remotes only (head/cat/mv) — use \`bash\` with a remote SSH command for Windows hosts`,
		);
	}
	if (!info.transferShell) {
		throw new Error(
			`ssh://: ${target.name} has no verified POSIX shell for ssh:// read/write — none of sh/bash/zsh round-tripped a capability probe (use \`bash\` with a remote SSH command for this host)`,
		);
	}
	return info.transferShell;
}

export interface RemoteFileReadOptions {
	/** Maximum bytes to materialize; the helper fetches one extra byte to detect truncation. */
	maxBytes: number;
	signal?: AbortSignal;
	timeoutMs?: number;
}

export interface RemoteFileReadResult {
	/** Raw file bytes, capped at `maxBytes`. */
	bytes: Uint8Array;
	/** True when the remote file was larger than `maxBytes` (`bytes` is the prefix). */
	truncated: boolean;

View on GitHub (pinned to 9690622007)

Solutions

  1. Give the account a real POSIX login shell (chsh -s /bin/bash user) on the remote
  2. Remove anything from shell rc files that prints output on non-interactive shells (echo, motd scripts) so the marker round-trips
  3. Check sshd_config for ForceCommand or Match restrictions blocking the probe
  4. Fall back to the bash tool with a remote SSH command for this host
  5. If the remote is busybox, ensure /bin/sh exists and behaves POSIXly

Example fix

# before (remote)
user:x:1000:1000::/home/user:/sbin/nologin
# after (on the remote)
$ sudo chsh -s /bin/bash user
Defensive patterns

Strategy: validation

Validate before calling

import { $ } from "bun";
const MARKER = "omp-probe-8f3a";
for (const sh of ["sh", "bash", "zsh"]) {
  const r = await $`ssh ${targetArg} ${sh} -lc 'echo ${MARKER}'`.quiet().nothrow();
  if (r.exitCode === 0 && r.stdout.toString().includes(MARKER)) return; // POSIX shell verified
}
throw new Error("no POSIX shell on remote; use bash tool with remote ssh command");

Try / catch

try {
  await upload(target, path, data);
} catch (err) {
  if (err instanceof Error && err.message.includes("no verified POSIX shell")) {
    // fall back to remote command execution for this host
    return runRemoteCommand(target, `tee ${path} > /dev/null`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Target host is POSIX-detected but its host info lacks transferShell: remotes with no standard shells in PATH for non-interactive sessions, restricted shells (rbash, nologin shell), custom minimal images (e.g. busybox with odd sh behavior or shells stripped), or a probe blocked by login banners/forced commands.

Common situations: Appliance/embedded devices (routers, NAS) with restricted firmware shells, accounts whose login shell is nologin/scponly, .bashrc outputting text that breaks the marker round-trip, ForceCommand in sshd_config.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/0a172cb163382d5d. Report an issue: GitHub.