{"record":{"id":"df531c90107fee6b","repo":"can1357/oh-my-pi","slug":"ssh-user-port-overrides-are-not-allowed-for-th","errorCode":null,"errorMessage":"ssh://: user/port overrides are not allowed for the configured host \"${decodedBareHost}\"; use ssh://${bareHost}/<path> or an unconfigured hostname","messagePattern":"ssh://: user/port overrides are not allowed for the configured host \"(.+?)\"; use ssh://(.+?)/<path> or an unconfigured hostname","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/ssh-protocol.ts","lineNumber":224,"sourceCode":"\t// where `url.password === \"\"`). `rawHost` keeps the literal marker, so it differs\n\t// from the canonical decoded `[user@]host[:port]` WHATWG actually parsed. Every\n\t// valid authority — including percent-encoded reserved-char aliases — reconstructs\n\t// to exactly `rawHost`, so only malformed userinfo trips this.\n\tconst canonicalAuthority = `${url.username ? `${decodeOr(url.username)}@` : \"\"}${decodeOr(bareHost)}${port !== undefined ? `:${port}` : \"\"}`;\n\tif (url.rawHost !== canonicalAuthority) {\n\t\tthrow new Error(\n\t\t\t`ssh://: unsupported or malformed authority in \"${url.href}\"; use ssh://[user@]host[:1-65535]/<absolute-path>`,\n\t\t);\n\t}\n\tconst items = await loadConfiguredHosts(cwd);\n\n\t// A literal user/port in the URL is an authority override. A configured alias\n\t// is addressed only by its (percent-encoded) name, never with a separate\n\t// user/port — so reject an override on a configured bare name, else opaque.\n\tif (username || port !== undefined) {\n\t\tconst decodedBareHost = decodeOr(bareHost);\n\t\tif (items.some(entry => entry.name === bareHost || entry.name === decodedBareHost)) {\n\t\t\tthrow new Error(\n\t\t\t\t`ssh://: user/port overrides are not allowed for the configured host \"${decodedBareHost}\"; use ssh://${bareHost}/<path> or an unconfigured hostname`,\n\t\t\t);\n\t\t}\n\t\tconst sshUser = username ? decodeOr(username) : undefined;\n\t\tconst sshTargetHost = decodeOr(sshHost);\n\t\tconst name = `${sshUser ? `${sshUser}@` : \"\"}${sshTargetHost}${port !== undefined ? `:${port}` : \"\"}`;\n\t\treturn { name, host: sshTargetHost, username: sshUser, port };\n\t}\n\n\t// No explicit user/port: match the full decoded authority against a\n\t// configured name (so an encoded reserved-char alias resolves correctly).\n\tconst match = items.find(entry => entry.name === rawAuthority) ?? items.find(entry => entry.name === bareHost);\n\tif (match) {\n\t\treturn {\n\t\t\tname: match.name,\n\t\t\thost: match.host,\n\t\t\tusername: match.username,\n\t\t\tport: match.port,","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/ssh-protocol.ts#L206-L242","documentation":"A literal user or port in an ssh:// URL is treated as an authority override on an OpenSSH destination. But a host name that matches a configured ssh.json entry is addressed only by its name — overrides would bypass the stored key/port settings and defeat the ControlMaster cache, which keys on the host name, so the handler rejects the combination.","triggerScenarios":"Resolving e.g. `ssh://root@prod/path` or `ssh://prod:2222/path` where `prod` is the `name` of an entry in the ssh.json capability file (matched against either the raw or decoded bare host).","commonSituations":"Trying to connect to a configured alias as a different user; assuming the alias can be parameterized with :port; forgetting that configured hosts carry their own username/port/key settings.","solutions":["Address the configured host by name only: `ssh://prod/path` — its user/port come from the ssh.json entry","Update the ssh.json entry if you need different user/port/key settings for that host","Use a real (unconfigured) hostname with user/port for a one-off override connection"],"exampleFix":"// before\nresolve('ssh://root@prod/etc/hosts') // 'prod' is a configured alias\n// after\nresolve('ssh://prod/etc/hosts') // or edit prod's ssh.json entry","handlingStrategy":"validation","validationCode":"const configured = new Set((await capability.loadCapability<SSHHost>('ssh', { cwd })).items.flatMap(h => [h.name, decodeURIComponent(h.name)]));\nconst u = new URL(candidate);\nif (u.protocol === 'ssh:' && (u.username || u.port) && configured.has(u.hostname)) throw new Error(`'${u.hostname}' is configured; address it by name without user/port overrides`);","typeGuard":"function isPlainConfiguredHost(u: URL, configured: Set<string>): boolean { return !(u.username || u.port) || !configured.has(u.hostname); }","tryCatchPattern":"try {\n  const res = await handler.resolve(url, ctx);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('user/port overrides are not allowed')) {\n    // drop user/port and use the alias, or edit the ssh.json entry, then retry\n  } else throw e;\n}","preventionTips":["Treat configured ssh.json names as opaque aliases — no user@ or :port on them","Change user/port/key in the ssh.json entry rather than overriding per-URL","Use raw hostnames (not configured names) when you need ad-hoc overrides"],"tags":["ssh","configuration","authority-override"],"backgroundTag":"ssh-configured-alias-override","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}