{"record":{"id":"0bef312e3bc5ebb8","repo":"can1357/oh-my-pi","slug":"failed-to-fetch-marketplace-catalog-from-source","errorCode":null,"errorMessage":"Failed to fetch marketplace catalog from ${source}: HTTP ${response.status} ${response.statusText}","messagePattern":"Failed to fetch marketplace catalog from (.+?): HTTP (.+?) (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/marketplace/fetcher.ts","lineNumber":267,"sourceCode":"\t\tconst resolved = path.resolve(expandHome(source));\n\t\tconst { catalogPath, content } = await readMarketplaceCatalog(resolved);\n\t\tconst catalog = parseMarketplaceCatalog(content, catalogPath);\n\t\treturn { catalog };\n\t}\n\n\tif (type === \"github\") {\n\t\tconst url = `https://github.com/${source}.git`;\n\t\treturn cloneAndReadCatalog(url, source, cacheDir);\n\t}\n\n\tif (type === \"git\") {\n\t\treturn cloneAndReadCatalog(source, source, cacheDir);\n\t}\n\n\t// type === \"url\"\n\tconst response = await fetch(source, { signal: AbortSignal.timeout(60_000) });\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`Failed to fetch marketplace catalog from ${source}: HTTP ${response.status} ${response.statusText}`,\n\t\t);\n\t}\n\tconst text = await response.text();\n\tconst catalog = parseMarketplaceCatalog(text, source);\n\n\tconst catalogDir = path.join(cacheDir, catalog.name);\n\tawait Bun.write(path.join(catalogDir, \"marketplace.json\"), text);\n\n\treturn { catalog };\n}\n\n// ── cloneAndReadCatalog ───────────────────────────────────────────────\n\n/**\n * Clone a git repository and read its marketplace catalog.\n *\n * Clones to a temporary directory and reads the catalog. The caller is","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/marketplace/fetcher.ts#L249-L285","documentation":"fetchMarketplace performs an HTTP GET of a URL-type marketplace source with a 60-second timeout and throws this when the response status is not OK (2xx). The message includes the URL, HTTP status code, and status text so the exact server rejection is visible.","triggerScenarios":"fetchMarketplace(\"https://...\") where the server returns 404 (wrong URL), 403 (private repo/forbidden), 500 (server error), or any other non-2xx status.","commonSituations":"A typo in the raw catalog URL; pointing at an HTML page instead of the raw JSON (e.g. a GitHub blob page instead of raw.githubusercontent.com); the catalog was moved/renamed upstream; a private URL without credentials; corporate proxy blocking the request.","solutions":["Open the URL in a browser or with `curl -I` and confirm it returns 200 with JSON; fix the URL (use the raw content URL for GitHub-hosted catalogs).","If the resource requires authentication, use a source type that supports credentials (e.g. git source with configured auth) instead of a plain URL.","If the catalog moved, update the source or remove and re-add the marketplace with the new URL; retry later for transient 5xx errors."],"exampleFix":"// before: HTML page URL\nawait fetchMarketplace(\"https://github.com/org/repo/blob/main/marketplace.json\", cacheDir);\n// after: raw content URL\nawait fetchMarketplace(\"https://raw.githubusercontent.com/org/repo/main/marketplace.json\", cacheDir);","handlingStrategy":"retry","validationCode":"const res = await fetch(url, { method: \"HEAD\" });\nif (!res.ok) throw new Error(`Catalog URL unreachable: HTTP ${res.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  const catalog = await fetchMarketplace(url, cacheDir);\n} catch (err) {\n  if ((err as Error).message.includes(\"HTTP \")) {\n    const status = Number((err as Error).message.match(/HTTP (\\d+)/)?.[1]);\n    if (status >= 500) { /* retry with backoff */ }\n    else { /* fix URL / auth — do not retry 4xx */ }\n  } else throw err;\n}","preventionTips":["Use raw-content URLs for hosted catalogs (raw.githubusercontent.com), not HTML pages.","Check the URL returns 200 with curl or a browser before registering it.","For authenticated resources, prefer a git source with configured credentials.","Distinguish 4xx (fix the request) from 5xx (retry later)."],"tags":["network","http","marketplace","fetch"],"backgroundTag":"http-non-ok-response","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}