can1357/oh-my-pi · error · Error

Invalid path: ${localPath} resolves outside working director

Error message

Invalid path: ${localPath} resolves outside working directory

What it means

linkPlugin links a local directory into the plugins tree for development. To prevent path traversal it resolves the supplied localPath against cwd and requires the result to be equal to or inside the resolved cwd. This error is thrown when the resolved absolute path escapes the working directory.

Source

Thrown at packages/coding-agent/src/extensibility/plugins/installer.ts:150

				manifest: pkg.omp || pkg.pi || { version: pkg.version },
				enabledFeatures: null,
				enabled: true,
			});
		}
	}

	return plugins;
}

export async function linkPlugin(localPath: string): Promise<void> {
	const cwd = getProjectDir();
	const absolutePath = path.resolve(cwd, localPath);

	// Validate that resolved path is within cwd to prevent path traversal
	const normalizedCwd = path.resolve(cwd);
	const normalizedPath = path.resolve(absolutePath);
	if (!normalizedPath.startsWith(`${normalizedCwd}/`) && normalizedPath !== normalizedCwd) {
		throw new Error(`Invalid path: ${localPath} resolves outside working directory`);
	}

	// Validate package.json exists
	const pkgFile = Bun.file(path.join(absolutePath, "package.json"));
	if (!(await pkgFile.exists())) {
		throw new Error(`package.json not found at ${absolutePath}`);
	}

	let pkg: { name?: string };
	try {
		pkg = await pkgFile.json();
	} catch (err) {
		throw new Error(`Invalid package.json at ${absolutePath}: ${err}`);
	}

	if (!pkg.name || typeof pkg.name !== "string") {
		throw new Error("package.json must have a valid name field");
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Move or symlink the plugin directory inside the current working directory and link that path
  2. Pass a path relative to cwd that does not traverse upward with `..`
  3. If the plugin legitimately lives elsewhere, copy it into the project rather than linking
  4. Use the project's supported mechanism for external plugins (install from npm/git) instead of linkPlugin

Example fix

// before (escapes cwd)
await linkPlugin("../../shared/my-plugin");
// after (path inside cwd)
await linkPlugin("plugins-local/my-plugin");
Defensive patterns

Strategy: validation

Validate before calling

const abs = path.resolve(cwd, localPath);
const inside = abs === path.resolve(cwd) || abs.startsWith(path.resolve(cwd) + path.sep);
if (!inside) throw new Error(`refusing to link outside cwd: ${localPath}`);
if (!(await Bun.file(path.join(abs, "package.json")).exists())) {
	throw new Error(`not a package root (no package.json): ${abs}`);
}

Try / catch

try {
	await linkPlugin(localPath);
} catch (err) {
	if (err instanceof Error && err.message.includes("resolves outside working directory")) {
		// copy the plugin into the project or use a supported install mechanism
	}
	throw err;
}

Prevention

When it happens

Trigger: linkPlugin(localPath, cwd) is called with a path containing `..` segments, an absolute path in a different tree, or a symlink-resolving parent that normalizes outside cwd — e.g. linkPlugin("../../shared-plugin") or linkPlugin("/opt/plugins/foo", cwd).

Common situations: Keeping plugin source outside the project (a monorepo sibling directory) and passing a relative path that climbs out; absolute paths pasted from elsewhere; CI working directories that differ from local ones so a previously-valid relative path now escapes.

Related errors


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