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

  1. Read the appended stderr tail in the message — it names the concrete curl failure; match it against curl's exit codes.
  2. Fix credentials: ensure options username/password (via credentials) match the FTP account, and the account may write to the target path.
  3. 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.
  4. 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.
  5. 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

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


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