{"record":{"id":"acc86fa6f0f19953","repo":"can1357/oh-my-pi","slug":"invalid-url-encoding-in-ssh-path-url-href","errorCode":null,"errorMessage":"Invalid URL encoding in ssh:// path: ${url.href}","messagePattern":"Invalid URL encoding in ssh:// path: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/ssh-protocol.ts","lineNumber":93,"sourceCode":"\t// (`ssh://h/tmp/a?draft` → `/tmp/a`). Reject the unsupported suffix instead of\n\t// silently operating on the truncated path; a literal `?`/`#` in a filename\n\t// must be percent-encoded (`%3F`/`%23`).\n\tif (url.search) {\n\t\tthrow new Error(\n\t\t\t`ssh:// does not support URL query strings; percent-encode a literal '?' as %3F in the path: ${url.href}`,\n\t\t);\n\t}\n\tif (url.hash) {\n\t\tthrow new Error(\n\t\t\t`ssh:// does not support URL fragments; percent-encode a literal '#' as %23 in the path: ${url.href}`,\n\t\t);\n\t}\n\tconst raw = url.rawPathname ?? url.pathname;\n\tlet decoded: string;\n\ttry {\n\t\tdecoded = decodeURIComponent(raw);\n\t} catch {\n\t\tthrow new Error(`Invalid URL encoding in ssh:// path: ${url.href}`);\n\t}\n\tif (!decoded) {\n\t\tthrow new Error(\n\t\t\t\"ssh:// requires an absolute path, e.g. ssh://host/etc/hosts or ssh://host/ for the root directory\",\n\t\t);\n\t}\n\treturn decoded;\n}\n\n/** Load the configured SSH hosts from the `ssh` capability (managed/project `ssh.json`). */\nasync function loadConfiguredHosts(cwd?: string): Promise<SSHHost[]> {\n\tconst { items } = await capability.loadCapability<SSHHost>(sshCapability.id, cwd ? { cwd } : {});\n\treturn items;\n}\n\n/** One-line address for a host, e.g. `deploy@10.0.0.1:2222`. */\nfunction hostAddress(host: SSHHost): string {\n\treturn `${host.username ? `${host.username}@` : \"\"}${host.host}${host.port ? `:${host.port}` : \"\"}`;","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/ssh-protocol.ts#L75-L111","documentation":"The remote path is obtained by percent-decoding `url.rawPathname ?? url.pathname`. If the pathname contains a malformed percent-escape (e.g. `%ZZ`, a lone `%`), `decodeURIComponent` throws, and the handler fails closed with this error rather than forwarding a mangled path to the remote shell.","triggerScenarios":"`SshProtocolHandler.resolve()` or `.write()` (via `remotePathFromUrl`) with a `ssh://` URL whose pathname contains an invalid percent-escape sequence, such as `ssh://host/tmp/100%done.txt` or `ssh://host/a%2b%`.","commonSituations":"Programmatic string interpolation of paths into ssh:// URLs without `encodeURIComponent`; a literal `%` in a filename (e.g. progress or format strings) left unencoded; double-encoding mistakes that leave stray `%` characters.","solutions":["Percent-encode path segments with `encodeURIComponent` before embedding them in the URL.","Encode a literal `%` as `%25` (e.g. `ssh://host/tmp/100%25done.txt`).","Check the URL for stray or truncated `%` characters and fix or remove them.","If the string came from user input, validate/encode it at the boundary instead of concatenating raw paths."],"exampleFix":"// before\nconst url = `ssh://host/tmp/${name}`; // name = \"100%done.txt\"\n// after\nconst url = `ssh://host/tmp/${encodeURIComponent(name)}`;","handlingStrategy":"validation","validationCode":"function buildSshUrl(host: string, remotePath: string): string {\n  return `ssh://${host}${remotePath.split(\"/\").map(encodeURIComponent).join(\"/\")}`;\n}","typeGuard":"function hasValidPercentEncoding(s: string): boolean {\n  try { decodeURIComponent(s); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  return await sshHandler.resolve(url);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Invalid URL encoding in ssh:// path\")) {\n    // rebuild the URL with encodeURIComponent on each path segment\n    return sshHandler.resolve(parseInternalUrl(buildSshUrl(host, rawPath)));\n  }\n  throw err;\n}","preventionTips":["Always encodeURIComponent() path segments built from variables.","Encode literal '%' as %25.","Never concatenate raw remote paths into ssh:// URLs.","Add a decodeURIComponent round-trip check on URLs built programmatically."],"tags":["url","ssh-protocol","encoding","percent-encoding"],"backgroundTag":"invalid-url-encoding","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}