can1357/oh-my-pi · error · DestinationUnavailableError

the configured command binary does not exist

Error message

the configured command binary does not exist

What it means

A DestinationUnavailableError thrown during ftp/ftps upload when spawning the configured commandBinary fails with ENOENT — i.e. the path/name in options.commandBinary does not exist or is not executable on this machine. It is raised inside upload() because Bun.spawn rejects at call time, wrapped from the generic Error into a destination-unavailable signal.

Source

Thrown at packages/coding-agent/src/blob-broker/uploaders-self-hosted.ts:261

			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");
	httpBase(publicBase, "publicBaseUrl");
	return {
		destination: "shared-folder",
		async upload(request) {
			const filename = safeFileName(request);
			const target = path.resolve(root, ...pathParts(directory), filename);

View on GitHub (pinned to 9690622007)

Solutions

  1. Locate the real curl path (which curl / command -v curl) and put that absolute path in options.commandBinary.
  2. Install curl in the runtime environment where uploads execute.
  3. If the binary exists, fix permissions (chmod +x) and confirm the broker process's PATH includes its directory.
  4. Verify with a manual spawn from the same environment: /path/to/curl --version should succeed.

Example fix

// before
{ "options": { "protocol": "ftp", "commandBinary": "/usr/local/bin/curl", ... } }  // ENOENT in container
// after
{ "options": { "protocol": "ftp", "commandBinary": "/usr/bin/curl", ... } }
Defensive patterns

Strategy: validation

Validate before calling

import { $which } from '@oh-my-pi/pi-utils';
const bin = dest.options?.commandBinary;
if (bin && !(await $which(bin))) throw new Error(`commandBinary not found or not executable: ${bin}`);

Try / catch

try {
  await uploader.upload(request);
} catch (err) {
  if (err?.name === 'DestinationUnavailableError' && /command binary does not exist/.test(err.message)) {
    // fail fast with operator guidance: fix commandBinary path or install curl
  } else throw err;
}

Prevention

When it happens

Trigger: options.commandBinary points to a nonexistent file (e.g. /usr/local/bin/curl on a system that has /usr/bin/curl), a bare name not on the PATH of the agent process, or the binary exists but lacks the execute bit.

Common situations: Config authored on macOS (/usr/local/bin/curl via Homebrew) running in a Linux container; CI images without curl; PATH differences between the developer shell and the daemon running the broker; Docker minimal images.

Related errors


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