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

  1. Percent-encode path segments with `encodeURIComponent` before embedding them in the URL.
  2. Encode a literal `%` as `%25` (e.g. `ssh://host/tmp/100%25done.txt`).
  3. Check the URL for stray or truncated `%` characters and fix or remove them.
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/acc86fa6f0f19953. Report an issue: GitHub.