can1357/oh-my-pi · error

imageUrls exposure "ssh" requires imageUrls.publicBaseUrl

Error message

imageUrls exposure "ssh" requires imageUrls.publicBaseUrl

What it means

The ssh exposure performs a reverse forward (ssh -R) to a remote host; a fronting web server on that host serves the blobs, and omp must know its public origin. This error throws when config.publicBaseUrl is absent for kind "ssh". The URL cannot be derived from the ssh target alone because the fronting server may use any scheme/host.

Source

Thrown at packages/coding-agent/src/blob-broker/exposure.ts:488

				const configFile = optionString(config, "configFile");
				const tunnelName = optionString(config, "tunnelName");
				if (!configFile || !tunnelName) {
					throw new Error(
						'imageUrls exposure "named-cloudflared" requires credentials.tunnelToken or options.configFile and options.tunnelName',
					);
				}
				argv = [binary, "tunnel", "--no-autoupdate", "--config", configFile, "run", tunnelName];
			}
			const baseUrl = normalizeBaseUrl(config.publicBaseUrl);
			const { proc } = await spawnUrlTunnel(
				argv,
				() => baseUrl,
				/Registered tunnel connection|Connection [a-z0-9-]+ registered/i,
			);
			return processExposure("named-cloudflared", baseUrl, proc);
		}
		case "ssh": {
			if (!config.publicBaseUrl) throw new Error('imageUrls exposure "ssh" requires imageUrls.publicBaseUrl');
			if (!config.sshTarget) throw new Error('imageUrls exposure "ssh" requires imageUrls.sshTarget');
			const binary = requireBinary("ssh");
			const remotePort = config.sshRemotePort ?? 8787;
			const proc = Bun.spawn(
				[
					binary,
					"-o",
					"BatchMode=yes",
					"-o",
					"ExitOnForwardFailure=yes",
					"-N",
					"-R",
					`${remotePort}:127.0.0.1:${port}`,
					config.sshTarget,
				],
				{ env: process.env, stdin: "ignore", stdout: "ignore", stderr: "ignore", cwd: os.homedir() },
			);
			const early = await Promise.race([

View on GitHub (pinned to 9690622007)

Solutions

  1. Add imageUrls.publicBaseUrl, e.g. "https://myserver.example.com" or "http://203.0.113.5:8787".
  2. Ensure the remote web server actually fronts that origin at sshRemotePort (default 8787).
  3. Verify no trailing slash in publicBaseUrl.

Example fix

// before
"imageUrls": { "exposure": { "kind": "ssh", "sshTarget": "user@host" } }
// after
"imageUrls": { "publicBaseUrl": "https://host.example.com", "exposure": { "kind": "ssh", "sshTarget": "user@host" } }
Defensive patterns

Strategy: validation

Validate before calling

if (config.kind === "ssh") {
  if (typeof config.publicBaseUrl !== "string" || !/^https?:\/\/[^/]+$/.test(config.publicBaseUrl)) {
    throw new Error("ssh exposure needs publicBaseUrl of the fronting web server");
  }
}

Prevention

When it happens

Trigger: Selecting exposure kind "ssh" without setting imageUrls.publicBaseUrl in the config.

Common situations: User configured sshTarget/sshRemotePort but forgot publicBaseUrl; assumed the ssh host is auto-detected; copied a tunnel-kind config and switched kind to ssh.

Understand the failure class

Background: "Must pass :limit option" / "Missing required option" — required option errors explained — this error's family across 41 libraries.

Related errors


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