can1357/oh-my-pi · error · Error

Plugin "${entry.name}" dapAdapters path escapes the plugin d

Error message

Plugin "${entry.name}" dapAdapters path escapes the plugin directory

What it means

Analogous to the lspServers check: when a plugin's dapAdapters field is a string, it names a debug-adapter config file (JSON or YAML) resolved against the installed plugin directory. #writeEmbeddedDapConfig enforces pathIsWithin(cachePath, sourcePath) so a plugin cannot read config files from outside its own directory, then copies it to .dap.json/.dap.yaml/.dap.yml inside the plugin cache path.

Source

Thrown at packages/coding-agent/src/extensibility/plugins/marketplace/manager.ts:396

			if (!pathIsWithin(cachePath, sourcePath)) {
				throw new Error(`Plugin "${entry.name}" lspServers path escapes the plugin directory`);
			}
			const content = await Bun.file(sourcePath).text();
			await Bun.write(targetPath, content);
			return;
		}

		await Bun.write(targetPath, `${JSON.stringify({ servers: lspServers }, null, 2)}\n`);
	}

	async #writeEmbeddedDapConfig(entry: MarketplacePluginEntry, cachePath: string): Promise<void> {
		const dapAdapters = entry.dapAdapters;
		if (!dapAdapters) return;

		if (typeof dapAdapters === "string") {
			const sourcePath = path.resolve(cachePath, dapAdapters);
			if (!pathIsWithin(cachePath, sourcePath)) {
				throw new Error(`Plugin "${entry.name}" dapAdapters path escapes the plugin directory`);
			}
			const extension = path.extname(sourcePath).toLowerCase();
			const targetFilename = extension === ".yaml" || extension === ".yml" ? `.dap${extension}` : ".dap.json";
			const targetPath = path.join(cachePath, targetFilename);
			const content = await Bun.file(sourcePath).text();
			await Bun.write(targetPath, content);
			return;
		}

		const targetPath = path.join(cachePath, ".dap.json");
		await Bun.write(targetPath, `${JSON.stringify({ adapters: dapAdapters }, null, 2)}\n`);
	}

	/**
	 * Resolve plugin version from multiple sources:
	 * 1. Catalog entry version (if set)
	 * 2. Plugin manifest (.claude-plugin/plugin.json, Agent Plugins root plugin.json, or package.json)
	 * 3. Git SHA from source (truncated to 7 chars)

View on GitHub (pinned to 9690622007)

Solutions

  1. Point dapAdapters at a config file inside the plugin directory ("./dap.json" or "./dap.yaml")
  2. Inline the debug adapter definitions as an object in the dapAdapters field instead of a path
  3. Copy the YAML/JSON config into the plugin's own directory and republish
  4. Ask the maintainer to fix the manifest path

Example fix

// before (catalog entry)
"dapAdapters": "../../debug/adapters.yaml"
// after
"dapAdapters": "./dap.yaml"  // or an inline object of adapter definitions
Defensive patterns

Strategy: validation

Validate before calling

import * as path from "node:path";
function dapPathIsContained(cachePath: string, dapAdapters: string | object | undefined): boolean {
  if (typeof dapAdapters !== "string") return true;
  const resolved = path.resolve(cachePath, dapAdapters);
  return resolved === cachePath || resolved.startsWith(cachePath + path.sep);
}
if (!dapPathIsContained(cachePath, entry.dapAdapters)) throw new Error("dapAdapters escapes plugin dir");

Type guard

function isContainedDapAdapters(v: unknown, cachePath: string): v is string {
  return typeof v === "string" && (path.resolve(cachePath, v) === cachePath || path.resolve(cachePath, v).startsWith(cachePath + path.sep));
}

Try / catch

try {
  await manager.installPlugin(name, marketplace);
} catch (err) {
  if (err instanceof Error && err.message.includes("dapAdapters path escapes")) {
    logger.error("Refusing plugin: insecure dapAdapters path", { name, marketplace });
  } else throw err;
}

Prevention

When it happens

Trigger: installPlugin on a plugin whose catalog entry sets dapAdapters to a string path that resolves outside cachePath — e.g. "../dap/adapters.yaml" or an absolute path elsewhere on disk.

Common situations: Plugin authors referencing a shared debug-adapter config in a sibling directory; absolute paths baked into manifests from another machine; symlinks escaping the plugin directory.

Related errors


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