can1357/oh-my-pi · error · Error
Unknown plugin source type: "${(source as { source: string }
Error message
Unknown plugin source type: "${(source as { source: string }).source}" What it means
The default branch of resolveObjectSource's switch fires when a plugin entry's source object declares a source type the resolver does not recognize — anything other than "url", "github", "git-subdir", or "npm". It reports the unrecognized value verbatim so the offending catalog entry can be found and corrected. Unlike the npm case, this usually signals a typo, an invalid/foreign catalog format, or catalog JSON not conforming to the PluginSource schema.
Source
Thrown at packages/coding-agent/src/extensibility/plugins/marketplace/source-resolver.ts:142
const subdirPath = path.resolve(cloneDir, source.path);
if (!pathIsWithin(cloneDir, subdirPath)) {
await fs.rm(cloneDir, { recursive: true, force: true });
throw new Error(`git-subdir path "${source.path}" escapes the cloned repository`);
}
try {
await verifyDirExists(subdirPath, `git-subdir path "${source.path}" does not exist in cloned repository`);
} catch (err) {
await fs.rm(cloneDir, { recursive: true, force: true });
throw err;
}
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
- Open the marketplace catalog and change the entry's source field to one of the supported values: "url", "github", "git-subdir" (string relative paths like "./plugins/foo" are also valid).
- Check spelling and case — the match is exact, so "Github" or "git-sub" fail; correct to lowercase exact literals.
- If the entry came from another tool's catalog format, translate it to this resolver's PluginSource schema rather than copying it verbatim.
- Validate the catalog JSON against the PluginSource type/schema before loading to catch the bad discriminator early.
Example fix
// before
{ "name": "my-plugin", "source": "git", "url": "https://github.com/owner/repo.git" }
// after
{ "name": "my-plugin", "source": "url", "url": "https://github.com/owner/repo.git" } Defensive patterns
Strategy: validation
Validate before calling
const KNOWN_SOURCE_TYPES = new Set(["url", "github", "git-subdir", "npm"]);
function hasKnownSourceType(entry: { source: unknown }): boolean {
if (typeof entry.source === "string") return entry.source.startsWith("./");
if (typeof entry.source === "object" && entry.source !== null) {
const t = (entry.source as { source?: unknown }).source;
return typeof t === "string" && KNOWN_SOURCE_TYPES.has(t);
}
return false;
}
// reject/warn on entries failing this check before calling resolvePluginSource Type guard
function hasRecognizedSource(source: unknown): boolean {
if (typeof source === "string") return source.startsWith("./");
if (typeof source !== "object" || source === null) return false;
const t = (source as { source?: unknown }).source;
return t === "url" || t === "github" || t === "git-subdir" || t === "npm";
} Try / catch
try {
const { dir } = await resolvePluginSource(entry, context);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
const m = msg.match(/^Unknown plugin source type: "(.*)"$/);
if (m) {
// report the exact bad type (m[1]) and the offending catalog entry; skip or abort
} else throw err;
} Prevention
- Use exact lowercase literals: "url", "github", "git-subdir" (or a "./"-prefixed string)
- Run marketplace.json through a schema validator with an enum on the source discriminator before install
- Translate foreign catalog formats to this resolver's schema rather than copying entries verbatim
- Re-validate catalogs after upgrading, in case the source-type vocabulary changed
When it happens
Trigger: resolvePluginSource is called with an object source whose .source string matches no case, e.g. { "source": "git" } instead of "git-subdir", { "source": "local", "path": ... }, { "source": "NPM" } (wrong case), or an entirely foreign shape from another tool's marketplace format; also malformed entries where the discriminator field holds a typo or the JSON was hand-edited.
Common situations: Typos in marketplace.json ("gitub", "gitsubdir", "Git-Hub"); copying plugin entries from Claude Code or other ecosystems whose source vocabularies differ; schema drift after a resolver update renamed or dropped a source type; build tooling emitting a wrong discriminator value.
Related errors
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE must contain a JSON object
- OMP_AUTH_BROKER_ACCOUNT_POOL_FILE entry for ${provider} must
- Plugin source "${source}" resolves outside marketplace root
- npm plugin sources are not yet supported. Use git-based sour
- Plugin source directory does not exist: "${resolved}"
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/fa1efa25dd507434.
Report an issue: GitHub.