can1357/oh-my-pi · error · Error
${protocol.toUpperCase()} upload command exited with code ${
Error message
${protocol.toUpperCase()} upload command exited with code ${exitCode}: ${stderr.trim().slice(-300)} What it means
Thrown from the ftp/ftps uploader's upload() when the spawned curl commandBinary finishes with a non-zero exit code. The message embeds the protocol, exit code, and the last 300 characters of curl's stderr so the operator can see the actual FTP failure (login refused, host unreachable, TLS error, etc.).
Source
Thrown at packages/coding-agent/src/blob-broker/uploaders-self-hosted.ts:255
"--ftp-create-dirs",
"--upload-file",
"-",
"--user",
`${username}:${password}`,
];
if (protocol === "ftps" && port !== 990) args.push("--ssl-reqd");
args.push(ftpUploadUrl(protocol, host, port, destinationPath));
try {
const process = Bun.spawn(args, {
stdin: request.bytes,
stdout: "ignore",
stderr: "pipe",
cwd: getSafeProjectCwd(),
});
const stderr = await new Response(process.stderr as ReadableStream<Uint8Array>).text();
const exitCode = await process.exited;
if (exitCode !== 0) {
throw new Error(
`${protocol.toUpperCase()} upload command exited with code ${exitCode}: ${stderr.trim().slice(-300)}`,
);
}
} catch (error) {
if (errorCode(error) === "ENOENT") {
throw new DestinationUnavailableError("ftp", "the configured command binary does not exist");
}
throw error;
}
return publication("ftp", request, publicUrl(publicBase, directory, filename));
},
};
}
function createSharedFolderUploader(config: DestinationRuntimeConfig): BlobUploader {
const root = path.resolve(requiredStringOption(config, "root"));
const directory = optionString(config, "path");
const publicBase = requiredStringOption(config, "publicBaseUrl");View on GitHub (pinned to 9690622007)
Solutions
- Read the appended stderr tail in the message — it names the concrete curl failure; match it against curl's exit codes.
- Fix credentials: ensure options username/password (via credentials) match the FTP account, and the account may write to the target path.
- For ftps, confirm the server's TLS mode: implicit TLS uses port 990, explicit TLS needs --ssl-reqd (already added when port != 990); adjust options.port accordingly.
- Test the same transfer manually: cat file | curl --fail --silent --show-error --ftp-create-dirs --upload-file - --user user:pass ftp://host/path to reproduce and debug.
- Check network/firewall (passive port range) and DNS if stderr shows connection or resolution errors.
Example fix
// before (manual repro showing exit 67 login refused) curl --upload-file - --user u:wrongpass ftp://ftp.example.com/dir/file.txt // after curl --upload-file - --user u:correctpass ftp://ftp.example.com/dir/file.txt # exit 0, then rerun the upload
Defensive patterns
Strategy: try-catch
Try / catch
try {
await uploader.upload(request);
} catch (err) {
if (err instanceof Error && /upload command exited with code/.test(err.message)) {
const m = err.message.match(/code (\d+):/);
const curlCode = m ? Number(m[1]) : null;
// map curlCode (6=DNS, 7=conn refused, 35=TLS, 67=login denied) to actionable remediation
if (curlCode !== 67) retryWithBackoff(); // transient network failures only
} else throw err;
} Prevention
- Pre-flight the FTP server: verify credentials and a test upload with the same curl flags in CI.
- Match options.port and protocol to the server's TLS mode (implicit 990 vs explicit 21 with --ssl-reqd).
- Ensure the FTP account has write access and passive-mode port ranges are open in firewalls.
- Keep the stderr tail from the error message — it usually names the exact server-side failure.
When it happens
Trigger: Any curl invocation during an ftp/ftps upload that exits non-zero: wrong credentials (curl exit 67), couldn't resolve host (6), connection refused (7), SSL/TLS handshake failure for ftps (35), permission to create dirs denied, or remote path invalid.
Common situations: Misconfigured FTP credentials; firewall/NAT blocking passive FTP; ftps server requiring explicit TLS on port 21 (uploader adds --ssl-reqd automatically when port != 990, but server may not support it); curl not compiled with FTPS support; read-only remote directory.
Related errors
- ${protocol.toUpperCase()} requires options.commandBinary poi
- the configured command binary does not exist
- brew update failed with exit code ${update.exitCode}
- timed out: {command}
- V2 remote compaction failed (${response.status} ${response.s
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/9a83a0076a16f649.
Report an issue: GitHub.