{"record":{"id":"262daf0ea721639b","repo":"can1357/oh-my-pi","slug":"failed-to-parse-marketplace-catalog-at-filepath","errorCode":null,"errorMessage":"Failed to parse marketplace catalog at ${filePath}: ${(err as Error).message}","messagePattern":"Failed to parse marketplace catalog at (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/marketplace/fetcher.ts","lineNumber":108,"sourceCode":"\t\tthrow new Error(`Missing or invalid field \"${field}\" in catalog: ${filePath}`);\n\t}\n}\n\n/**\n * Parse and validate a marketplace.json catalog from raw JSON content.\n *\n * Required fields: name (valid name segment), owner.name, plugins array.\n * Each plugin entry requires name (string) and source (string or object\n * with a \"source\" field). Extra fields are preserved via spread.\n *\n * @throws on JSON parse failure or missing/invalid required fields.\n */\nexport function parseMarketplaceCatalog(content: string, filePath: string): MarketplaceCatalog {\n\tlet raw: unknown;\n\ttry {\n\t\traw = JSON.parse(content);\n\t} catch (err) {\n\t\tthrow new Error(`Failed to parse marketplace catalog at ${filePath}: ${(err as Error).message}`);\n\t}\n\n\tif (!raw || typeof raw !== \"object\" || Array.isArray(raw)) {\n\t\tthrow new Error(`Marketplace catalog at ${filePath} must be a JSON object`);\n\t}\n\n\tconst obj = raw as Record<string, unknown>;\n\n\t// name: required, must be a valid name segment\n\tassertField(typeof obj.name === \"string\" && isValidNameSegment(obj.name), \"name\", filePath);\n\n\t// owner: required object with name string\n\tassertField(typeof obj.owner === \"object\" && obj.owner !== null && !Array.isArray(obj.owner), \"owner\", filePath);\n\tconst owner = obj.owner as Record<string, unknown>;\n\tassertField(typeof owner.name === \"string\", \"owner.name\", filePath);\n\n\t// plugins: required array\n\tassertField(Array.isArray(obj.plugins), \"plugins\", filePath);","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/marketplace/fetcher.ts#L90-L126","documentation":"parseMarketplaceCatalog wraps JSON.parse failures when reading a marketplace catalog file. It means the catalog at the given path is not syntactically valid JSON. The underlying JSON parse message is appended so the exact syntax problem (unexpected token, bad escape, truncation) is visible.","triggerScenarios":"parseMarketplaceCatalog(content, filePath) called with content that JSON.parse rejects: malformed JSON from a manual edit, an HTML error page saved as catalog.json, a truncated download, or comments/trailing commas in the file.","commonSituations":"Hand-editing the catalog JSON and leaving a trailing comma; a proxy or captive portal returning an HTML error page that got saved to the catalog path; a partial git clone/file sync leaving a truncated file; editing on Windows introducing a BOM or smart quotes.","solutions":["Open the file at the reported path and fix the JSON syntax error indicated by the appended parse message (use a JSON linter or `bun -e 'JSON.parse(require(\"fs\").readFileSync(path))'`).","If the file was fetched from a URL, re-fetch and verify it returns application/json, not an HTML error page.","Restore the catalog from the upstream marketplace repository or remove and re-add the marketplace so it is re-cloned."],"exampleFix":"// before (catalog.json with trailing comma)\n{ \"name\": \"my-market\", \"plugins\": [ ... ], }\n// after\n{ \"name\": \"my-market\", \"plugins\": [ ... ] }","handlingStrategy":"try-catch","validationCode":"try { JSON.parse(content); } catch (e) { console.error(`Catalog content is not valid JSON: ${(e as Error).message}`); }","typeGuard":null,"tryCatchPattern":"try {\n  const catalog = parseMarketplaceCatalog(content, filePath);\n} catch (err) {\n  if ((err as Error).message.startsWith(\"Failed to parse marketplace catalog\")) {\n    // surface filePath + cause message, fall back to a default catalog or prompt re-fetch\n  } else throw err;\n}","preventionTips":["Validate catalog JSON with a linter before committing or serving it.","Never hand-edit catalogs without re-running a JSON parse check.","For URL sources, confirm the endpoint returns Content-Type: application/json.","Avoid trailing commas and comments — marketplace catalogs are strict JSON."],"tags":["json","parse-error","marketplace","config"],"backgroundTag":"invalid-json-syntax","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}