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

  1. 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).
  2. Check spelling and case — the match is exact, so "Github" or "git-sub" fail; correct to lowercase exact literals.
  3. If the entry came from another tool's catalog format, translate it to this resolver's PluginSource schema rather than copying it verbatim.
  4. 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

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


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