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
- Add imageUrls.publicBaseUrl, e.g. "https://myserver.example.com" or "http://203.0.113.5:8787".
- Ensure the remote web server actually fronts that origin at sshRemotePort (default 8787).
- 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
- Set publicBaseUrl whenever the exposure kind is ssh
- Confirm the remote fronting server actually serves that origin at sshRemotePort
- Validate exposure config at startup, before spawning ssh
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
- imageUrls exposure "ssh" requires imageUrls.sshTarget
- imageUrls exposure "named-cloudflared" requires imageUrls.pu
- No known OAuth callback port for '${provider}'. Use device-c
- ssh://: user/port overrides are not allowed for the configur
- SSH key not found: ${keyPath}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/62f5001eabc9615c.
Report an issue: GitHub.