can1357/oh-my-pi · error · Error

Marketplace " " already exists

Error message

Marketplace "${entry.name}" already exists

What it means

addMarketplaceEntry() is a pure registry transform that enforces unique marketplace names. If the registry already contains a marketplace with the same name, it throws instead of silently overwriting the existing entry (which would lose the old source/catalogPath). Callers are expected to remove or update the entry explicitly.

Solutions

  1. Check existence first with getMarketplaceEntry() and skip or replace the entry
  2. Remove the existing marketplace (removeMarketplaceEntry) then re-add with the desired source
  3. Use an update flow that mutates the existing entry in place instead of appending
  4. Choose a different marketplace name if both sources are wanted

Example fix

// before
reg = addMarketplaceEntry(reg, entry); // throws if exists
// after
if (!getMarketplaceEntry(reg, entry.name)) {
  reg = addMarketplaceEntry(reg, entry);
} else {
  reg = removeMarketplaceEntry(reg, entry.name);
  reg = addMarketplaceEntry(reg, entry);
}
Defensive patterns

Strategy: validation

Validate before calling

import { getMarketplaceEntry, addMarketplaceEntry, removeMarketplaceEntry } from './registry';
if (getMarketplaceEntry(reg, entry.name)) {
  reg = removeMarketplaceEntry(reg, entry.name);
}
reg = addMarketplaceEntry(reg, entry);

Type guard

null

Try / catch

try {
  reg = addMarketplaceEntry(reg, entry);
} catch (err) {
  if (err.message === `Marketplace "${entry.name}" already exists`) {
    reg = removeMarketplaceEntry(reg, entry.name);
    reg = addMarketplaceEntry(reg, entry); // replace
  } else throw err;
}

Prevention

When it happens

Trigger: Calling addMarketplaceEntry() with a name already present in reg.marketplaces — e.g. re-running a "marketplace add" for an existing marketplace, or an update flow that adds instead of replacing.

Common situations: Re-running a setup script that adds marketplaces idempotently but does not check existence; adding a marketplace whose name collides with a previously registered one from a different source; a partially-failed update that left the entry in place.

Related errors


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

Appendix: source

Thrown at packages/coding-agent/src/extensibility/plugins/marketplace/registry.ts:136

		// Accept any numeric version — forward compatible reads
		return { ...data, version: 2 };
	} catch (err) {
		if (isEnoent(err)) return emptyInstalledPluginsRegistry();
		throw err;
	}
}

export async function writeInstalledPluginsRegistry(filePath: string, reg: InstalledPluginsRegistry): Promise<void> {
	await atomicWriteJson(filePath, reg);
}

// ── Marketplace CRUD ─────────────────────────────────────────────────
// Pure functions that transform registry state. Caller is responsible for
// reading, mutating, and writing back.

export function addMarketplaceEntry(reg: MarketplacesRegistry, entry: MarketplaceRegistryEntry): MarketplacesRegistry {
	if (reg.marketplaces.some(m => m.name === entry.name)) {
		throw new Error(`Marketplace "${entry.name}" already exists`);
	}
	return { ...reg, marketplaces: [...reg.marketplaces, entry] };
}

export function removeMarketplaceEntry(reg: MarketplacesRegistry, name: string): MarketplacesRegistry {
	const filtered = reg.marketplaces.filter(m => m.name !== name);
	if (filtered.length === reg.marketplaces.length) {
		throw new Error(`Marketplace "${name}" not found`);
	}
	return { ...reg, marketplaces: filtered };
}

export function getMarketplaceEntry(reg: MarketplacesRegistry, name: string): MarketplaceRegistryEntry | undefined {
	return reg.marketplaces.find(m => m.name === name);
}

// ── Installed plugin CRUD ────────────────────────────────────────────

View on GitHub (pinned to 9690622007)