can1357/oh-my-pi · error · DestinationUnavailableError
SFTP password injection is unsupported by the shared SSH tra
Error message
SFTP password injection is unsupported by the shared SSH transport; configure a private-key path or SSH agent
What it means
A DestinationUnavailableError thrown when an sftp destination defines a password credential but no privateKey path. The ftp uploader routes SFTP through the shared SSH transport (writeRemoteFile), which only supports key files or SSH agent auth; interactive password injection is not implemented, so the destination is declared unavailable at creation time.
Source
Thrown at packages/coding-agent/src/blob-broker/uploaders-self-hosted.ts:191
}
function createFtpUploader(config: DestinationRuntimeConfig): BlobUploader {
const protocol = optionString(config, "protocol", "sftp");
if (protocol !== "ftp" && protocol !== "ftps" && protocol !== "sftp") {
throw new Error("Destination option protocol must be ftp, ftps, or sftp");
}
const host = requiredStringOption(config, "host");
const username = requireCredential(config, "username");
const directory = optionString(config, "path");
const publicBase = requiredStringOption(config, "publicBaseUrl");
httpBase(publicBase, "publicBaseUrl");
if (protocol === "sftp") {
const port = optionNumber(config, "port", 22) ?? 22;
const keyPath = credentialString(config, "privateKey");
const password = credentialString(config, "password");
if (password && !keyPath) {
throw new DestinationUnavailableError(
"ftp",
"SFTP password injection is unsupported by the shared SSH transport; configure a private-key path or SSH agent",
);
}
if (keyPath?.includes("-----BEGIN")) {
throw new DestinationUnavailableError(
"ftp",
"the SFTP privateKey credential must be a filesystem path, not key contents",
);
}
const connectionName = `blob-${username}-${host}-${port}`.replace(/[^A-Za-z0-9._-]/g, "-");
return {
destination: "ftp",
async upload(request) {
const filename = safeFileName(request);
await writeRemoteFile(
{ name: connectionName, host, username, port, ...(keyPath ? { keyPath } : {}) },
remotePath(directory, filename),View on GitHub (pinned to 9690622007)
Solutions
- Set credentials.privateKey to the filesystem path of an SSH private key (e.g. ~/.ssh/id_ed25519).
- Or rely on SSH agent auth: still set a privateKey path if your agent holds the key, or use a key loaded in the agent without a password credential.
- If you only have password access to the server, switch options.protocol to "ftp" or "ftps" (curl path supports user:password).
- Add the key to ssh-agent (ssh-add) if you want agent-based auth alongside the configured key path.
Example fix
// before
{ "destination": "ftp", "options": { "protocol": "sftp", "host": "box.example.com" }, "credentials": { "username": "u", "password": "hunter2" } }
// after
{ "destination": "ftp", "options": { "protocol": "sftp", "host": "box.example.com" }, "credentials": { "username": "u", "privateKey": "/home/me/.ssh/id_ed25519" } } Defensive patterns
Strategy: validation
Validate before calling
if (dest.protocol === 'sftp' && dest.credentials?.password && !dest.credentials?.privateKey) {
throw new Error('sftp destinations need credentials.privateKey (path) or SSH agent; password-only is unsupported');
} Try / catch
try {
const uploader = createSelfHostedUploader('ftp', config);
} catch (err) {
if (err?.name === 'DestinationUnavailableError' && /password injection is unsupported/.test(err.message)) {
// reconfigure with a privateKey path or switch protocol to ftp/ftps
} else throw err;
} Prevention
- Provision SSH key-based auth on servers and never rely on passwords for SFTP automation.
- Keep credentials.privateKey as a path convention; load key material into ssh-agent where possible.
- Run a pre-flight validation of sftp destinations (protocol + credentials combination) at config load.
- Use password auth only on ftp/ftps (curl) destinations, where it is supported.
When it happens
Trigger: options.protocol = "sftp" with credentials.password set and credentials.privateKey absent (or falsy), when createFtpUploader builds the uploader for destination "ftp".
Common situations: Reusing an FTP (curl-based) config's password credential on an SFTP destination; assuming password auth works because plain ftp/ftps supports it; missing that privateKey must be set even when ssh-agent is available (the check only requires a key path to be configured).
Related errors
- ssh reverse forward to ${config.sshTarget} exited with code
- ssh://: password authentication is not supported; ssh:// use
- SSH key not found: ${keyPath}
- SSH key is not a file: ${keyPath}
- --list-details, --exec, and --exec-batch are not supported b
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f94465e7a0e2d3e9.
Report an issue: GitHub.