can1357/oh-my-pi · error · Error

${argv[0]} printed no URL on stdout

Error message

${argv[0]} printed no URL on stdout

What it means

After the upload command exits successfully (code 0), extractUploadUrl() scans stdout for a URL. If none is found, the uploader cannot produce a BlobPublication and throws this error naming the program. The command must print the resulting upload URL to stdout.

Source

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

					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 });
				const timeout = setTimeout(() => proc.kill(), UPLOAD_TIMEOUT_MS);
				const [stdout, stderr, exitCode] = await Promise.all([
					new Response(proc.stdout as ReadableStream<Uint8Array>).text(),
					new Response(proc.stderr as ReadableStream<Uint8Array>).text(),
					proc.exited,
				]);
				clearTimeout(timeout);
				if (exitCode !== 0) {
					throw new Error(`${argv[0]} exited with code ${exitCode}: ${stderr.trim().slice(-300)}`);
				}
				const url = extractUploadUrl(stdout);
				if (!url) throw new Error(`${argv[0]} printed no URL on stdout`);
				return { url, destination: "command", bytes: bytes.byteLength };
			} finally {
				await fs.rm(file, { force: true });
			}
		},
	};
}

/**
 * Resolve one registry destination to its built-in uploader.
 *
 * Serving destinations deliberately return `null`; the broker selects those
 * through its separate serve-kind predicate. Registry entries known to be
 * unusable, and active entries without an implementation, fail explicitly
 * before an upload can issue a network request.
 */
export function createConfiguredUploader(
	destination: BlobDestinationId,

View on GitHub (pinned to 9690622007)

Solutions

  1. Make the command print the URL to stdout, e.g. append `| tail -1` or use the tool's quiet/url-only mode
  2. Check whether the tool prints the URL to stderr and redirect: `tool {file} 2>&1`
  3. Ensure the output contains a plain http(s) URL the extractor can find (avoid ANSI colors: add --no-color or pipe through sed)
  4. Test locally: run the command and confirm a bare URL appears on stdout

Example fix

// before
"urls": { "command": "imgur-uploader {file} --json" }
// after
"urls": { "command": "imgur-uploader {file} --json | jq -r .link" }
Defensive patterns

Strategy: validation

Validate before calling

// verify the tool prints a bare URL on stdout before configuring:
const out = Bun.spawnSync(["your-uploader", "/tmp/test.png"], { stdout: "pipe" }).stdout.toString();
if (!/https?:\/\/\S+/.test(out)) throw new Error("uploader must print an http(s) URL to stdout");

Try / catch

try {
	await uploader.upload(req);
} catch (err) {
	if (err instanceof Error && err.message.includes("printed no URL on stdout")) {
		// check where the tool emits the URL and normalize the command (jq/sed, 2>&1)
	}
	throw err;
}

Prevention

When it happens

Trigger: The configured command exits 0 but prints nothing to stdout, prints only human-readable text/progress to stdout, sends the URL to stderr or a file, or prints a URL format extractUploadUrl does not recognize (e.g. URL embedded in ANSI-colored or JSON output not matched by the extractor).

Common situations: Using a tool that prints results to stderr by default; wrapping the tool in a script that swallows stdout; a tool whose success output is a JSON blob the regex doesn't match; redirecting URL output to a file in the command template.

Related errors


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