{"record":{"id":"eb1a4c40fe2bd4f7","repo":"can1357/oh-my-pi","slug":"ssh-port-0-is-not-a-valid-ssh-port-use-ssh","errorCode":null,"errorMessage":"ssh://: port 0 is not a valid SSH port; use ssh://host:<1-65535>/<path> or omit the port","messagePattern":"ssh://: port 0 is not a valid SSH port; use ssh://host:<1-65535>/<path> or omit the port","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/ssh-protocol.ts","lineNumber":177,"sourceCode":"\t\tif (part.includes(\"%\")) {\n\t\t\ttry {\n\t\t\t\tdecodeURIComponent(part);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`ssh://: invalid percent-escape in authority \"${url.href}\"`);\n\t\t\t}\n\t\t}\n\t}\n\tif (url.password) {\n\t\tthrow new Error(\n\t\t\t\"ssh://: password authentication is not supported; ssh:// uses key/agent auth — drop the ':<password>' from the URL\",\n\t\t);\n\t}\n\tconst isIpv6Literal = bareHost.startsWith(\"[\") && bareHost.endsWith(\"]\");\n\tconst sshHost = isIpv6Literal ? bareHost.slice(1, -1) : bareHost;\n\tconst username = url.username || undefined;\n\tconst port = url.port ? Number(url.port) : undefined;\n\tif (port === 0) {\n\t\tthrow new Error(\"ssh://: port 0 is not a valid SSH port; use ssh://host:<1-65535>/<path> or omit the port\");\n\t}\n\t// An empty port (`ssh://prod:/path`, `ssh://user@host:/path`, including\n\t// percent-encoded authority parts) parses cleanly with `url.port === \"\"`, so it\n\t// slips past the malformed-authority guard and would be read as \"no port\" —\n\t// silently using the default/configured target. `url.rawHost` is the decoded\n\t// authority and uniquely retains the trailing `:`; comparing it to the decoded\n\t// host (+ user) catches the empty port, while a percent-encoded alias like\n\t// `prod%3A` (whose decoded host already ends in `:`) reconstructs to `prod::`\n\t// and is left alone.\n\tconst decodeOr = (s: string): string => {\n\t\ttry {\n\t\t\treturn decodeURIComponent(s);\n\t\t} catch {\n\t\t\treturn s;\n\t\t}\n\t};\n\tif (port === undefined && url.rawHost === `${username ? `${decodeOr(username)}@` : \"\"}${decodeOr(bareHost)}:`) {\n\t\tthrow new Error(`ssh://: empty port in \"${url.href}\"; use ssh://host:<1-65535>/<path> or drop the colon`);","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/ssh-protocol.ts#L159-L195","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nresolve('ssh://prod.example.com:0/etc/hosts')\n// after\nresolve('ssh://prod.example.com:22/etc/hosts')","handlingStrategy":"validation","validationCode":"const u = new URL(candidate);\nif (u.protocol === 'ssh:' && u.port !== '' && Number(u.port) === 0) throw new Error('ssh port must be 1-65535; drop :0 or fix the port');","typeGuard":"function hasValidSshPort(u: URL): boolean { const p = Number(u.port); return u.port === '' || (Number.isInteger(p) && p >= 1 && p <= 65535); }","tryCatchPattern":"try {\n  const res = await handler.resolve(url, ctx);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('port 0 is not a valid SSH port')) {\n    // omit or correct the port, then retry\n  } else throw e;\n}","preventionTips":["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"],"tags":["ssh","port","url-validation"],"backgroundTag":"invalid-port-number","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}