can1357/oh-my-pi · error · Error

Plugin source directory does not exist: "${resolved}"

Error message

Plugin source directory does not exist: "${resolved}"

What it means

verifyDirExists is the final disk check performed after a plugin source resolves to a candidate directory. It stats the path and throws the supplied message when the path does not exist (ENOENT) or exists but is not a directory; other stat errors (permission denied, etc.) are rethrown unchanged. For relative sources and git-subdir sources this message names the fully resolved path, so it means: the source resolution succeeded, but nothing usable (a directory) is at that location.

Source

Thrown at packages/coding-agent/src/extensibility/plugins/marketplace/source-resolver.ts:152

			}
			return { dir: subdirPath, tempCloneRoot: cloneDir };
		}

		case "npm":
			throw new Error("npm plugin sources are not yet supported. Use git-based sources instead.");

		default:
			throw new Error(`Unknown plugin source type: "${(source as { source: string }).source}"`);
	}
}

// ── Helpers ─────────────────────────────────────────────────────────

async function verifyDirExists(dirPath: string, errorMessage: string): Promise<void> {
	try {
		const stat = await fs.stat(dirPath);
		if (!stat.isDirectory()) {
			throw new Error(errorMessage);
		}
	} catch (err) {
		if (isEnoent(err)) {
			throw new Error(errorMessage);
		}
		throw err;
	}
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the exact directory exists at the resolved path shown in the message; fix the catalog source path (or pluginRoot) to match the actual layout, e.g. "./plugins/foo" vs "./foo".
  2. Update the marketplace clone (git pull / re-clone) or correct the pinned ref/sha so it points at a revision containing the directory.
  3. If the entry points at a file, point it at the plugin's directory instead — the resolver requires a directory containing the plugin.
  4. Check for sparse-checkout/submodule settings on the clone that exclude the path, and ensure read permissions on the directory.

Example fix

// before (.claude-plugin/marketplace.json)
{ "name": "formatter", "source": "./tools/formatter-plugin" }  // directory was renamed

// after (after confirming actual layout in the clone)
{ "name": "formatter", "source": "./plugins/formatter" }
Defensive patterns

Strategy: try-catch

Validate before calling

import * as fs from "node:fs/promises";

async function pluginDirExists(dirPath: string): Promise<boolean> {
  try {
    return (await fs.stat(dirPath)).isDirectory();
  } catch {
    return false;
  }
}
// pre-check resolved candidate paths before calling resolvePluginSource, when you can compute them

Try / catch

try {
  const { dir } = await resolvePluginSource(entry, context);
} catch (err) {
  const msg = err instanceof Error ? err.message : String(err);
  if (msg.startsWith("Plugin source directory does not exist:") || msg.includes("does not exist in cloned repository")) {
    // the resolved path from the message tells you exactly what to fix in the catalog entry
  } else throw err;
}

Prevention

When it happens

Trigger: Called from resolveRelativeSource (source: 1470 context) when the resolved path inside the marketplace clone is missing or is a file, and from resolveObjectSource's git-subdir branch when source.path does not exist in the cloned repo or is a file, not a directory. Triggering inputs: typo'd relative path in the catalog; plugin directory renamed/deleted/moved upstream; wrong ref/sha pinned to an older layout; entry pointing at a single file instead of a plugin directory; incomplete marketplace clone (shallow/partial checkout missing submodules or sparse-checkout exclusions).

Common situations: Catalog lists "./plugins/foo" but the repo's folder is "./plugin/foo" or was renamed in a refactor; marketplace.json updated upstream while the local clone is stale (or vice versa); pinned sha predates the directory's creation; plugin root set via catalogMetadata.pluginRoot prepended incorrectly so the joined path misses; git-subdir path pointing at a README file rather than the plugin folder.

Related errors


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