can1357/oh-my-pi · error · Error
Marketplace catalog at ${filePath} must be a JSON object
Error message
Marketplace catalog at ${filePath} must be a JSON object What it means
After a successful JSON.parse, parseMarketplaceCatalog requires the parsed value to be a plain object. Arrays, strings, numbers, booleans, and null are rejected because a marketplace catalog must carry named fields (name, plugins, ...). The file parses fine but has the wrong top-level shape.
Source
Thrown at packages/coding-agent/src/extensibility/plugins/marketplace/fetcher.ts:112
/**
* Parse and validate a marketplace.json catalog from raw JSON content.
*
* Required fields: name (valid name segment), owner.name, plugins array.
* Each plugin entry requires name (string) and source (string or object
* with a "source" field). Extra fields are preserved via spread.
*
* @throws on JSON parse failure or missing/invalid required fields.
*/
export function parseMarketplaceCatalog(content: string, filePath: string): MarketplaceCatalog {
let raw: unknown;
try {
raw = JSON.parse(content);
} catch (err) {
throw new Error(`Failed to parse marketplace catalog at ${filePath}: ${(err as Error).message}`);
}
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
throw new Error(`Marketplace catalog at ${filePath} must be a JSON object`);
}
const obj = raw as Record<string, unknown>;
// name: required, must be a valid name segment
assertField(typeof obj.name === "string" && isValidNameSegment(obj.name), "name", filePath);
// owner: required object with name string
assertField(typeof obj.owner === "object" && obj.owner !== null && !Array.isArray(obj.owner), "owner", filePath);
const owner = obj.owner as Record<string, unknown>;
assertField(typeof owner.name === "string", "owner.name", filePath);
// plugins: required array
assertField(Array.isArray(obj.plugins), "plugins", filePath);
const plugins = obj.plugins as unknown[];
const validPlugins: unknown[] = [];
for (let i = 0; i < plugins.length; i++) {View on GitHub (pinned to 9690622007)
Solutions
- Wrap the catalog content in a JSON object with the required fields: { "name": "...", "plugins": [...] }.
- Check the upstream marketplace for the expected catalog schema and update your file to match.
- If pointing at a URL, ensure the endpoint returns the catalog object, not a list or scalar.
Example fix
// before (catalog.json)
[{ "name": "plugin-a" }]
// after
{ "name": "my-market", "plugins": [{ "name": "plugin-a" }] } Defensive patterns
Strategy: validation
Validate before calling
const parsed = JSON.parse(content);
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error("Catalog must be a JSON object with name and plugins fields");
}
if (typeof parsed.name !== "string" || !Array.isArray(parsed.plugins)) {
throw new Error("Catalog is missing required name/plugins fields");
} Type guard
function isCatalogObject(v: unknown): v is Record<string, unknown> {
return typeof v === "object" && v !== null && !Array.isArray(v);
} Prevention
- Keep the required top-level shape: { name, plugins: [...] } — never emit a bare array.
- Validate against the marketplace catalog schema in CI before publishing.
- When converting from another format, always wrap results in an object.
When it happens
Trigger: parseMarketplaceCatalog called with content that parses to a JSON array (e.g. the file contains only the plugins list), a bare string/number, or null.
Common situations: Writing just the plugins array to catalog.json instead of wrapping it in an object; a upstream repo restructuring the catalog to a different top-level shape; an API endpoint returning a JSON array of catalogs.
Related errors
- Missing or invalid field "${field}" in catalog: ${filePath}
- Failed to parse marketplace catalog at ${filePath}: ${(err a
- Invalid cmux socket response: expected object
- Invalid cmux socket response: missing ok flag
- ${XD_URL_PREFIX}${device.name} content must be a JSON object
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/346e80b89765a356.
Report an issue: GitHub.