can1357/oh-my-pi · error
Invalid URL encoding in ssh:// path: ${url.href}
Error message
Invalid URL encoding in ssh:// path: ${url.href} What it means
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.
Source
Thrown at packages/coding-agent/src/internal-urls/ssh-protocol.ts:93
// (`ssh://h/tmp/a?draft` → `/tmp/a`). Reject the unsupported suffix instead of
// silently operating on the truncated path; a literal `?`/`#` in a filename
// must be percent-encoded (`%3F`/`%23`).
if (url.search) {
throw new Error(
`ssh:// does not support URL query strings; percent-encode a literal '?' as %3F in the path: ${url.href}`,
);
}
if (url.hash) {
throw new Error(
`ssh:// does not support URL fragments; percent-encode a literal '#' as %23 in the path: ${url.href}`,
);
}
const raw = url.rawPathname ?? url.pathname;
let decoded: string;
try {
decoded = decodeURIComponent(raw);
} catch {
throw new Error(`Invalid URL encoding in ssh:// path: ${url.href}`);
}
if (!decoded) {
throw new Error(
"ssh:// requires an absolute path, e.g. ssh://host/etc/hosts or ssh://host/ for the root directory",
);
}
return decoded;
}
/** Load the configured SSH hosts from the `ssh` capability (managed/project `ssh.json`). */
async function loadConfiguredHosts(cwd?: string): Promise<SSHHost[]> {
const { items } = await capability.loadCapability<SSHHost>(sshCapability.id, cwd ? { cwd } : {});
return items;
}
/** One-line address for a host, e.g. `deploy@10.0.0.1:2222`. */
function hostAddress(host: SSHHost): string {
return `${host.username ? `${host.username}@` : ""}${host.host}${host.port ? `:${host.port}` : ""}`;View on GitHub (pinned to 9690622007)
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.
Example fix
// before
const url = `ssh://host/tmp/${name}`; // name = "100%done.txt"
// after
const url = `ssh://host/tmp/${encodeURIComponent(name)}`; Defensive patterns
Strategy: validation
Validate before calling
function buildSshUrl(host: string, remotePath: string): string {
return `ssh://${host}${remotePath.split("/").map(encodeURIComponent).join("/")}`;
} Type guard
function hasValidPercentEncoding(s: string): boolean {
try { decodeURIComponent(s); return true; } catch { return false; }
} Try / catch
try {
return await sshHandler.resolve(url);
} catch (err) {
if (err instanceof Error && err.message.startsWith("Invalid URL encoding in ssh:// path")) {
// rebuild the URL with encodeURIComponent on each path segment
return sshHandler.resolve(parseInternalUrl(buildSshUrl(host, rawPath)));
}
throw err;
} Prevention
- 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.
When it happens
Trigger: `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%`.
Common situations: 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.
Related errors
- ssh://: invalid percent-escape in authority "${url.href}"
- ssh:// does not support URL query strings; percent-encode a
- ssh:// does not support URL fragments; percent-encode a lite
- Invalid URL encoding in vault:// path: ${url.href}
- Invalid URL encoding in memory:// path: ${url.href}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/acc86fa6f0f19953.
Report an issue: GitHub.