{"record":{"id":"293297cd9c6acff7","repo":"can1357/oh-my-pi","slug":"marketplace-catalog-name-already-exists","errorCode":null,"errorMessage":"Marketplace \"${catalog.name}\" already exists","messagePattern":"Marketplace \"(.+?)\" already exists","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/marketplace/manager.ts","lineNumber":102,"sourceCode":"\t\tconst extra = this.#opts.projectInstalledRegistryPath\n\t\t\t? ([this.#opts.projectInstalledRegistryPath] as readonly string[])\n\t\t\t: undefined;\n\t\tthis.#opts.clearPluginRootsCache?.(extra);\n\t}\n\n\t// ── Marketplace lifecycle ─────────────────────────────────────────────────\n\n\tasync addMarketplace(source: string): Promise<MarketplaceRegistryEntry> {\n\t\tconst reg = await readMarketplacesRegistry(this.#opts.marketplacesRegistryPath);\n\t\tconst existingNames = new Set(reg.marketplaces.map(m => m.name));\n\n\t\tconst { catalog, clonePath } = await fetchMarketplace(source, this.#opts.marketplacesCacheDir);\n\n\t\tif (existingNames.has(catalog.name)) {\n\t\t\tif (clonePath) {\n\t\t\t\tawait fs.rm(clonePath, { recursive: true, force: true }).catch(() => {});\n\t\t\t}\n\t\t\tthrow new Error(`Marketplace \"${catalog.name}\" already exists`);\n\t\t}\n\n\t\t// Promote the temp clone to its final cache location now that we know it's not a duplicate.\n\t\tif (clonePath) {\n\t\t\tawait promoteCloneToCache(clonePath, this.#opts.marketplacesCacheDir, catalog.name);\n\t\t}\n\n\t\tconst sourceType = classifySource(source);\n\t\tconst normalizedSource = sourceType === \"local\" ? path.resolve(expandTilde(source)) : source;\n\n\t\tconst catalogPath = path.resolve(\n\t\t\texpandTilde(path.join(this.#opts.marketplacesCacheDir, catalog.name, \"marketplace.json\")),\n\t\t);\n\n\t\t// Persist the fetched catalog so subsequent reads don't require re-fetching.\n\t\tawait Bun.write(catalogPath, `${JSON.stringify(catalog, null, 2)}\\n`);\n\n\t\tconst now = new Date().toISOString();","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/marketplace/manager.ts#L84-L120","documentation":"addMarketplace fetches a new marketplace source and refuses to register it if a marketplace with the same catalog name already exists in the registry. It cleans up any temporary clone before throwing. Registry entries are keyed by catalog name, so duplicates would be ambiguous.","triggerScenarios":"MarketplaceManager.addMarketplace(source) called when the fetched catalog's `name` matches an entry already in the marketplaces registry — e.g. re-adding the same marketplace, or two different sources whose catalogs declare the same name.","commonSituations":"Running an add command twice; two forks of the same marketplace both named \"community\"; a curated marketplace and a personal mirror sharing a name; the built-in marketplace already registered and the user adding a source with the same name.","solutions":["If you want to refresh the existing marketplace, use updateMarketplace(name) instead of addMarketplace.","If the sources are genuinely different, rename the `name` field in one catalog so they are distinct.","Remove the existing marketplace first (removeMarketplace) and then re-add the desired source."],"exampleFix":"// before: duplicate add\nawait manager.addMarketplace(\"https://example.com/marketplace.json\"); // name \"community\" exists\n// after: update instead\nawait manager.updateMarketplace(\"community\");","handlingStrategy":"try-catch","validationCode":"const reg = JSON.parse(await Bun.file(registryPath).text());\n// after fetching/parsing the new catalog locally:\nif (reg.marketplaces.some(m => m.name === catalogName)) {\n  console.warn(`Marketplace \"${catalogName}\" already registered — call updateMarketplace instead`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await manager.addMarketplace(source);\n} catch (err) {\n  if ((err as Error).message.includes(\"already exists\")) {\n    // either treat as success (idempotent add) or route to updateMarketplace\n  } else throw err;\n}","preventionTips":["Make add operations idempotent in scripts: catch this and call updateMarketplace.","Avoid registering multiple catalogs that share the same `name`; rename forks/mirrors.","Check the registry listing before adding in interactive tools."],"tags":["duplicate","registry","marketplace","state-conflict"],"backgroundTag":"duplicate-entry","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}