can1357/oh-my-pi · error
ssh://: port 0 is not a valid SSH port; use ssh://host:<1-65
Error message
ssh://: port 0 is not a valid SSH port; use ssh://host:<1-65535>/<path> or omit the port
What it means
Port 0 is a reserved placeholder meaning 'pick an ephemeral port' and is never a valid destination port for sshd. The handler rejects it explicitly during authority resolution so the connection is not attempted against port 0, which would always fail.
Source
Thrown at packages/coding-agent/src/internal-urls/ssh-protocol.ts:177
if (part.includes("%")) {
try {
decodeURIComponent(part);
} catch {
throw new Error(`ssh://: invalid percent-escape in authority "${url.href}"`);
}
}
}
if (url.password) {
throw new Error(
"ssh://: password authentication is not supported; ssh:// uses key/agent auth — drop the ':<password>' from the URL",
);
}
const isIpv6Literal = bareHost.startsWith("[") && bareHost.endsWith("]");
const sshHost = isIpv6Literal ? bareHost.slice(1, -1) : bareHost;
const username = url.username || undefined;
const port = url.port ? Number(url.port) : undefined;
if (port === 0) {
throw new Error("ssh://: port 0 is not a valid SSH port; use ssh://host:<1-65535>/<path> or omit the port");
}
// An empty port (`ssh://prod:/path`, `ssh://user@host:/path`, including
// percent-encoded authority parts) parses cleanly with `url.port === ""`, so it
// slips past the malformed-authority guard and would be read as "no port" —
// silently using the default/configured target. `url.rawHost` is the decoded
// authority and uniquely retains the trailing `:`; comparing it to the decoded
// host (+ user) catches the empty port, while a percent-encoded alias like
// `prod%3A` (whose decoded host already ends in `:`) reconstructs to `prod::`
// and is left alone.
const decodeOr = (s: string): string => {
try {
return decodeURIComponent(s);
} catch {
return s;
}
};
if (port === undefined && url.rawHost === `${username ? `${decodeOr(username)}@` : ""}${decodeOr(bareHost)}:`) {
throw new Error(`ssh://: empty port in "${url.href}"; use ssh://host:<1-65535>/<path> or drop the colon`);View on GitHub (pinned to 9690622007)
Solutions
- Replace port 0 with the real SSH port (commonly 22 or a custom value like 2222)
- Drop the `:0` suffix entirely to use the default/configured port
- Check the upstream config/source that emitted the port to fix it where it is generated
Example fix
// before
resolve('ssh://prod.example.com:0/etc/hosts')
// after
resolve('ssh://prod.example.com:22/etc/hosts') Defensive patterns
Strategy: validation
Validate before calling
const u = new URL(candidate);
if (u.protocol === 'ssh:' && u.port !== '' && Number(u.port) === 0) throw new Error('ssh port must be 1-65535; drop :0 or fix the port'); Type guard
function hasValidSshPort(u: URL): boolean { const p = Number(u.port); return u.port === '' || (Number.isInteger(p) && p >= 1 && p <= 65535); } Try / catch
try {
const res = await handler.resolve(url, ctx);
} catch (e) {
if (e instanceof Error && e.message.includes('port 0 is not a valid SSH port')) {
// omit or correct the port, then retry
} else throw e;
} Prevention
- Validate port variables are 1-65535 before interpolating into ssh:// URLs
- Treat 0/undefined port as 'omit the suffix' in URL-building code
- Check generated configs for zeroed port fields
When it happens
Trigger: Resolving an ssh:// URL whose parsed port numerically equals 0, e.g. `ssh://prod.example.com:0/etc/hosts` (produced by a variable that evaluated to 0 or a template with a missing port value).
Common situations: A config generator substituting a falsy/missing port variable as 0; copy-pasted IPv6 or service entry where the port field was zeroed; hand-written URLs with an accidental 0.
Related errors
- ssh://: password authentication is not supported; ssh:// use
- imageUrls exposure "ssh" requires imageUrls.publicBaseUrl
- imageUrls exposure "ssh" requires imageUrls.sshTarget
- ssh reverse forward to ${config.sshTarget} exited with code
- resultBaseUrl is not a valid URL
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/eb1a4c40fe2bd4f7.
Report an issue: GitHub.