can1357/oh-my-pi · error · Error
Marketplace plugin package path escapes node_modules: ${JSON
Error message
Marketplace plugin package path escapes node_modules: ${JSON.stringify(packageName)} What it means
MarketplaceManager.#runtimePackagePath() computes the symlink path for a plugin's package inside the scope's node_modules directory. It first validates the package name with assertRuntimePackageName(), then double-checks via path.relative() that the resolved link path stays inside node_modules. If the name would escape (e.g. contains "..", absolute paths, or is empty), it throws this path-traversal defense error.
Source
Thrown at packages/coding-agent/src/extensibility/plugins/marketplace/manager.ts:811
}
async #resolvePluginPackageName(installPath: string, fallbackName: string): Promise<string> {
try {
const pkg: { name?: unknown } = await Bun.file(path.join(installPath, "package.json")).json();
const name = typeof pkg.name === "string" && pkg.name.length > 0 ? pkg.name : fallbackName;
return assertRuntimePackageName(name);
} catch (err) {
if (isEnoent(err)) return assertRuntimePackageName(fallbackName);
throw err;
}
}
#runtimePackagePath(scope: "user" | "project", packageName: string): string {
const nodeModules = path.resolve(this.#nodeModulesPath(scope));
const linkPath = path.resolve(nodeModules, assertRuntimePackageName(packageName));
const relative = path.relative(nodeModules, linkPath);
if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) {
throw new Error(`Marketplace plugin package path escapes node_modules: ${JSON.stringify(packageName)}`);
}
return linkPath;
}
async #resolveInstalledPackageNames(
entries: readonly InstalledPluginEntry[],
fallbackName: string,
): Promise<Set<string>> {
const packageNames = new Set<string>();
for (const entry of entries) {
packageNames.add(await this.#resolvePluginPackageName(entry.installPath, fallbackName));
}
return packageNames;
}
async #registerRuntimePlugin(
scope: "user" | "project",
packageName: string,View on GitHub (pinned to 9690622007)
Solutions
- Inspect the installed-plugins registry / marketplace catalog entry and fix the packageName field to a bare npm-style name
- Remove and reinstall the plugin so the registry is rewritten with a valid name
- Never hand-edit registry JSON — use the marketplace CLI commands to mutate state
- If a third-party marketplace produced this, report/remove that marketplace entry
Example fix
// before (registry entry)
{"packageName": "../../evil"}
// after
{"packageName": "my-plugin-pkg"} Defensive patterns
Strategy: validation
Validate before calling
import * as path from 'node:path';
function isSafePackageName(name) {
return typeof name === 'string' && /^[a-z0-9@][a-z0-9._\-/]*$/i.test(name) &&
!name.includes('..') && !path.isAbsolute(name);
} Type guard
const isSafePkgName = (v) => typeof v === 'string' && v.length > 0 && !v.includes('..') && !path.isAbsolute(v); Try / catch
try {
const linkPath = manager.resolveRuntimePackagePath(scope, pkg);
} catch (err) {
if (err.message.includes('escapes node_modules')) {
console.error(`Refusing unsafe package name ${pkg} — registry entry may be corrupted`);
} else throw err;
} Prevention
- Never hand-edit installed-plugins.json — mutate via manager APIs
- Validate package names when importing registries from untrusted sources
- Treat this error as a sign of tampering or corruption, not a code bug
- Reinstall the plugin to regenerate a clean registry entry
When it happens
Trigger: A marketplace catalog or installed entry contains a malicious or corrupt packageName such as "../../evil", an absolute path like "/etc", or an empty string; a manually edited installed-plugins.json with a tampered package field.
Common situations: Hand-edited or corrupted registry files; a malicious/misconfigured marketplace publishing a plugin entry with traversal in its package name; symlink-resolution oddities after moving project directories.
Related errors
- Destination paths cannot contain parent traversal or NUL byt
- Shared-folder destination escapes its configured root
- Absolute paths are not allowed in ${scheme}:// URLs: ${rawPa
- Path traversal (..) is not allowed in ${scheme}:// URLs: ${r
- Unsafe #{scheme}:// path (absolute or traversal): #{path}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ed526d16f2405b03.
Report an issue: GitHub.