{"record":{"id":"e45e40aad131dccd","repo":"can1357/oh-my-pi","slug":"missing-or-invalid-field-field-in-catalog","errorCode":null,"errorMessage":"Missing or invalid field \"${field}\" in catalog: ${filePath}","messagePattern":"Missing or invalid field \"(.+?)\" in catalog: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/marketplace/fetcher.ts","lineNumber":90,"sourceCode":"\n\t// Rule 4: Explicit relative or home-relative paths\n\tif (source.startsWith(\"./\") || source.startsWith(\"~/\")) {\n\t\treturn \"local\";\n\t}\n\n\t// Rule 5: Absolute paths — POSIX via path.isAbsolute, Windows via regex\n\tif (path.isAbsolute(source) || WIN_ABS_RE.test(source)) {\n\t\treturn \"local\";\n\t}\n\n\tthrow new Error(`Unrecognized source format. Did you mean './${source}' (local) or 'owner/repo' (GitHub)?`);\n}\n\n// ── parseMarketplaceCatalog ───────────────────────────────────────────\n\nfunction assertField(condition: boolean, field: string, filePath: string): void {\n\tif (!condition) {\n\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}`);","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/marketplace/fetcher.ts#L72-L108","documentation":"parseMarketplaceCatalog validates required fields of a fetched marketplace.json via assertField; when a required field is missing, null, or of the wrong shape, it throws this error naming the field and the file path it was parsed from. It is the schema-validation gate for marketplace catalog files.","triggerScenarios":"Fetching a marketplace.json that lacks required fields (e.g. name, plugins array) or has wrong types; pointing a marketplace source at the wrong JSON file; a catalog from an older/different schema version; truncated or hand-edited catalog JSON.","commonSituations":"Upstream marketplace repo restructured its catalog; user created a local marketplace.json missing required keys; typo in field name (case-sensitive); serving an HTML error page saved as .json that happens to parse partially.","solutions":["Open the file named in the error and add/fix the quoted field.","Compare against a known-good marketplace.json schema/example and match field names and types.","Update the marketplace repo/plugin if the catalog is outdated relative to the expected schema.","Validate the JSON parses fully and is not an error page or truncated download."],"exampleFix":"// before (marketplace.json)\n{ \"plugins\": [ { \"name\": \"a\", \"source\": \"acme/a\" } ] }\n// after (assuming \"name\" was the missing field)\n{ \"name\": \"acme-marketplace\", \"plugins\": [ { \"name\": \"a\", \"source\": \"acme/a\" } ] }","handlingStrategy":"validation","validationCode":"function validateCatalog(json: unknown): void {\n  const c = json as Record<string, unknown>;\n  if (typeof c.name !== \"string\" || c.name.length === 0) throw new Error(\"catalog.name required\");\n  if (!Array.isArray(c.plugins)) throw new Error(\"catalog.plugins must be an array\");\n  for (const p of c.plugins) {\n    const e = p as Record<string, unknown>;\n    if (typeof e.name !== \"string\" || typeof e.source !== \"string\") {\n      throw new Error(\"each plugin needs name and source\");\n    }\n  }\n}","typeGuard":"function isCatalog(v: unknown): v is { name: string; plugins: { name: string; source: string }[] } {\n  if (typeof v !== \"object\" || v === null) return false;\n  const c = v as Record<string, unknown>;\n  return typeof c.name === \"string\" && Array.isArray(c.plugins);\n}","tryCatchPattern":"try {\n  const result = await fetchMarketplace(source);\n} catch (err) {\n  const m = err instanceof Error && err.message.match(/Missing or invalid field \"([^\"]+)\" in catalog: (.*)/);\n  if (m) {\n    console.error(`Fix field \"${m[1]}\" in ${m[2]}`);\n  } else throw err;\n}","preventionTips":["Validate marketplace.json against the catalog schema before publishing/serving","Keep local catalogs in sync with the current expected schema","Fetch catalogs over reliable URLs and verify the response is JSON, not an error page","Test custom marketplaces with the parser before distributing them"],"tags":["marketplace","validation","schema","json","catalog"],"backgroundTag":"schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}