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
- Give the account a real POSIX login shell (chsh -s /bin/bash user) on the remote
- Remove anything from shell rc files that prints output on non-interactive shells (echo, motd scripts) so the marker round-trips
- Check sshd_config for ForceCommand or Match restrictions blocking the probe
- Fall back to the bash tool with a remote SSH command for this host
- 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
- Ensure service accounts get /bin/bash or /bin/sh, not nologin
- Keep rc files silent on non-interactive shells (guard with [[ $- == *i* ]])
- Remove ForceCommand restrictions for this account if transfers are needed
- Probe new hosts with `ssh host sh -lc 'echo ok'` before enabling ssh://
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
- Cannot ${internalUrlAction} a remote ssh:// path (no local f
- unknown function: {0}
- unknown key binding function: {0}
- unimplemented: {0}
- I/O error occurred
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/0a172cb163382d5d.
Report an issue: GitHub.