can1357/oh-my-pi · error

Invalid URL encoding in memory:// path: ${url.href}

Error message

Invalid URL encoding in memory:// path: ${url.href}

What it means

After stripping the leading slash, the memory:// pathname is percent-decoded with decodeURIComponent. If the escapes are malformed (e.g. a dangling '%' or a truncated '%E4' sequence), decoding throws and the resolver rethrows this error naming the offending URL, rather than producing a corrupted path.

Source

Thrown at packages/coding-agent/src/internal-urls/memory-protocol.ts:135

export function resolveMemoryUrlToPath(url: InternalUrl, memoryRoot: string): string {
	const namespace = url.rawHost || url.hostname;
	if (!namespace) {
		throw new Error("memory:// URL requires a namespace: memory://root");
	}
	if (namespace !== MEMORY_NAMESPACE) {
		throw new Error(`Unknown memory namespace: ${namespace}. Supported: ${MEMORY_NAMESPACE}`);
	}

	const rawPathname = url.rawPathname ?? url.pathname;
	const hasPath = rawPathname && rawPathname !== "/" && rawPathname !== "";
	if (!hasPath) {
		return path.resolve(memoryRoot, DEFAULT_MEMORY_FILE);
	}
	let relativePath: string;
	try {
		relativePath = decodeURIComponent(rawPathname.slice(1));
	} catch {
		throw new Error(`Invalid URL encoding in memory:// path: ${url.href}`);
	}

	try {
		validateRelativePath(relativePath);
	} catch (error) {
		throw toMemoryValidationError(error);
	}

	return path.resolve(memoryRoot, relativePath);
}

async function tryResolveInRoot(url: InternalUrl, memoryRoot: string): Promise<InternalResource | undefined> {
	const resolved = path.resolve(memoryRoot);
	let resolvedRoot: string;
	try {
		resolvedRoot = await fs.realpath(resolved);
	} catch (error) {
		if (isEnoent(error)) return undefined;

View on GitHub (pinned to 9690622007)

Solutions

  1. Percent-encode special characters properly: encode '%' as %25 and non-ASCII as valid UTF-8 escapes before putting them in the URL.
  2. Use encodeURIComponent on dynamic path segments when building memory:// URLs programmatically.
  3. If the '%' is meant literally in a filename, escape it as %25, e.g. memory://root/100%25done.md.

Example fix

// before
read(`memory://root/${rawName}`) // rawName = "50% off.md"
// after
read(`memory://root/${encodeURIComponent(rawName)}`)
Defensive patterns

Strategy: validation

Validate before calling

const pathname = url.rawPathname ?? url.pathname;
try { decodeURIComponent(pathname.slice(1)); } catch { throw new Error(`Invalid percent-encoding: ${url.href}`); }

Try / catch

try {
  const p = resolveMemoryUrlToPath(url, root);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Invalid URL encoding")) {
    // re-encode the path segment and retry once
  } else throw err;
}

Prevention

When it happens

Trigger: resolveMemoryUrlToPath receiving a pathname with invalid percent-encoding, such as memory://root/notes%.md or memory://root/%E4%B8 (truncated UTF-8 escape), usually from hand-written or shell-mangled URLs.

Common situations: Copy-pasting a URL where a '%' got eaten by a shell; double-encoding mistakes (writing %2520 then partially decoding); tools that interpolate raw strings containing '%' into the path.

Related errors


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