can1357/oh-my-pi · error

memory:// URL escapes memory root

Error message

memory:// URL escapes memory root

What it means

memory:// URLs may only address paths inside the configured memory root directory. ensureWithinRoot checks the target (and, in tryResolveInRoot, its symlink-resolved realpath) against the root prefix and throws if the resolved path would land outside it. This is a path-traversal guard: the URL's decoded path, or a symlink inside memory, pointed outside the sanctioned root.

Source

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

	const agentDir = getAgentDir();
	const roots: string[] = [];
	for (const ref of AgentRegistry.global().list()) {
		const sm = ref.session?.sessionManager;
		if (!sm) continue;
		const root = getMemoryRoot(agentDir, sm.getCwd());
		if (root && !roots.includes(root)) roots.push(root);
	}
	return roots;
}

function memoryRootsForContext(context?: ResolveContext): string[] {
	if (context?.cwd) return [getMemoryRoot(getAgentDir(), context.cwd)];
	return memoryRootsFromRegistry();
}

function ensureWithinRoot(targetPath: string, rootPath: string): void {
	if (targetPath !== rootPath && !targetPath.startsWith(`${rootPath}${path.sep}`)) {
		throw new Error("memory:// URL escapes memory root");
	}
}

function toMemoryValidationError(error: unknown): Error {
	const message = error instanceof Error ? error.message : String(error);
	return new Error(message.replace("skill://", "memory://"));
}

export interface MemoryGlobPattern {
	baseUrl: string;
	globPattern: string;
}

/**
 * Decode percent-escapes in a raw glob-suffix segment, bracket-escaping any
 * glob metacharacter that was percent-encoded so it stays a literal filename
 * character instead of becoming glob syntax.
 */

View on GitHub (pinned to 9690622007)

Solutions

  1. Replace any symlink inside the memory root with a real copy of the target file so realpath stays under the root.
  2. Decode the URL and confirm the path contains no ../ or absolute components; use a plain relative path under memory://root/.
  3. Check where getMemoryRoot places the root for your session cwd and keep target files inside that directory.
  4. If you need content from outside memory, read it directly by file path instead of through memory://.

Example fix

# before — symlink escapes the memory root
ln -s ~/docs/notes.md .omp/memories/notes.md
# after — real file inside the root
cp ~/docs/notes.md .omp/memories/notes.md && rm .omp/memories/notes.md.orig
Defensive patterns

Strategy: validation

Validate before calling

import * as path from "node:path";
import * as fs from "node:fs/promises";
const root = await fs.realpath(memoryRoot);
const target = path.resolve(root, decodeURIComponent(urlPath.replace(/^\//, "")));
const real = await fs.realpath(target).catch(() => target);
if (real !== root && !real.startsWith(root + path.sep)) {
  throw new Error(`refusing: ${urlPath} escapes memory root`);
}

Type guard

function isWithinRoot(targetPath: string, rootPath: string): boolean {
  return targetPath === rootPath || targetPath.startsWith(`${rootPath}${path.sep}`);
}

Try / catch

try {
  return await handler.resolve(url, context);
} catch (e) {
  if (e instanceof Error && e.message === "memory:// URL escapes memory root") {
    logger.warn("memory:// traversal blocked", { href: url.href });
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: A memory:// URL whose decoded path escapes via ../ segments that slip past validateRelativePath, or more commonly a symlink inside the memory root pointing to a file elsewhere on disk — the final fs.realpath check fails the prefix test and throws.

Common situations: Users symlinking memory files to notes elsewhere in their repo; project setups where .omp/memories contains links into shared drives; crafted memory:// URLs in agent output attempting traversal; copying a memory dir that contains stale symlinks after a move.

Related errors


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