can1357/oh-my-pi · error · Error

images.urls.command is empty

Error message

images.urls.command is empty

What it means

createCommandUploader builds a BlobUploader from an argv template string (images.urls.command in config). After splitting the template, if no arguments remain the command is useless, so it throws immediately. This is a synchronous, configuration-time validation error.

Source

Thrown at packages/coding-agent/src/blob-broker/uploaders.ts:82

}

/** Last URL printed on stdout wins; uploader tools often log progress first. */
export function extractUploadUrl(stdout: string): string | null {
	let last: string | null = null;
	for (const match of stdout.matchAll(URL_PATTERN)) {
		last = match[0].replace(/[)\],.'"]+$/, "");
	}
	return last;
}

/**
 * Build an uploader from an argv template. Placeholders, substituted after
 * splitting (paths with spaces stay one argument): `{file}` temp file path,
 * `{mime}` MIME type, `{ext}` bare extension.
 */
export function createCommandUploader(template: string): BlobUploader {
	const argvTemplate = splitCommandTemplate(template);
	if (argvTemplate.length === 0) throw new Error("images.urls.command is empty");
	if (!argvTemplate.some(arg => arg.includes("{file}"))) {
		throw new Error("images.urls.command must reference {file}");
	}
	return {
		destination: "command",
		async upload(request: BlobUploadRequest): Promise<BlobPublication> {
			const { bytes, mimeType, extension } = request;
			const file = path.join(os.tmpdir(), `omp-blob-upload-${crypto.randomUUID()}.${extension}`);
			await Bun.write(file, bytes);
			try {
				const argv = argvTemplate.map(arg =>
					arg.replaceAll("{file}", file).replaceAll("{mime}", mimeType).replaceAll("{ext}", extension),
				);
				const cwd = getProjectDir();
				if (!directoryIsEnterableSync(cwd)) {
					throw new Error(`Project directory is not accessible: ${cwd}`);
				}
				const proc = Bun.spawn(argv, { stdin: "ignore", stdout: "pipe", stderr: "pipe", cwd });

View on GitHub (pinned to 9690622007)

Solutions

  1. Set images.urls.command to a real command template, e.g. "curl -F 'file=@{file}' https://0x0.st"
  2. Remove the empty images.urls.command key if you do not want the command destination
  3. Check the config file section actually saved (empty string vs commented-out line)
  4. Validate the template contains {file} as well, since a non-empty template without {file} throws the next error

Example fix

// before
"urls": { "command": "" }
// after
"urls": { "command": "curl -sF 'file=@{file}' https://0x0.st" }
Defensive patterns

Strategy: validation

Validate before calling

const cmd = config.images?.urls?.command;
if (typeof cmd !== "string" || cmd.trim().length === 0) {
	throw new Error("images.urls.command must be a non-empty command template");
}

Type guard

function hasCommandTemplate(c: unknown): c is { command: string } {
	return typeof c === "object" && c !== null && typeof (c as { command?: unknown }).command === "string"
		&& (c as { command: string }).command.trim().length > 0;
}

Try / catch

try {
	const uploader = createConfiguredUploader("command", config);
} catch (err) {
	if (err instanceof Error && err.message === "images.urls.command is empty") {
		// prompt user to fill images.urls.command in config
	}
	throw err;
}

Prevention

When it happens

Trigger: Configuring images.urls.command as an empty string or whitespace-only string; the template consists solely of characters that splitCommandTemplate strips (quotes/spaces), yielding zero argv entries.

Common situations: Config placeholder left unfilled ("command": "") after copying a config template; YAML/TOML key present but value empty; quoting mistakes that collapse the value to nothing.

Related errors


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