{"record":{"id":"12bccffe93a6f8a6","repo":"nexu-io/open-design","slug":"petshare-list-page-page-failed-resp-status","errorCode":null,"errorMessage":"petshare list page ${page} failed: ${resp.status} ${resp.statusText}","messagePattern":"petshare list page (.+?) failed: (.+?) (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/daemon/src/community-pets-sync.ts","lineNumber":117,"sourceCode":"\nasync function pathExists(p: string): Promise<boolean> {\n  try {\n    await stat(p);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nasync function listPetSharePets(limit: number | null): Promise<PetTask[]> {\n  const tasks: PetTask[] = [];\n  let page = 1;\n  const pageSize = 24;\n  for (;;) {\n    const url = `${PETSHARE_BASE}/api/pets?page=${page}&pageSize=${pageSize}`;\n    const resp = await fetch(url);\n    if (!resp.ok) {\n      throw new Error(`petshare list page ${page} failed: ${resp.status} ${resp.statusText}`);\n    }\n    const data = (await resp.json()) as PetShareListResponse;\n    for (const pet of data.pets ?? []) {\n      const folder = sanitizeFolder(pet.id);\n      if (!folder) continue;\n      const spritesheetUrl = pet.spritesheetUrl?.startsWith('http')\n        ? pet.spritesheetUrl\n        : `${PETSHARE_BASE}${pet.spritesheetUrl ?? ''}`;\n      const ext = extOf(pet.spritesheetPath ?? spritesheetUrl);\n      tasks.push({\n        source: 'petshare',\n        folder,\n        manifest: {\n          id: pet.id,\n          displayName: pet.displayName,\n          description: pet.description ?? '',\n          spritesheetPath: `spritesheet.${ext}`,\n          author: pet.ownerName,","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/community-pets-sync.ts#L99-L135","documentation":"Thrown by listPetSharePets when the HTTP GET to the PetShare catalog API (PETSHARE_BASE/api/pets?page=N&pageSize=24) returns a non-2xx status. The error message includes the page number and the HTTP status+statusText for diagnostics. This is a transient external-dependency failure — the Supabase-hosted PetShare function is unreachable or returning an error.","triggerScenarios":"Calling the community pets sync (POST /api/codex-pets/sync with source 'petshare' or 'all') when the PetShare Supabase edge function returns 4xx/5xx. The fetch at line 115 gets a response, resp.ok is false, and the error is thrown at line 117.","commonSituations":"The PetShare Supabase function is rate-limited (429), under maintenance (503), or the function deployment is cold/timing out. A network proxy returns a 502. The PETSHARE_BASE URL has changed or the Supabase project is paused.","solutions":["Retry the sync after a short delay — PetShare outages are typically transient.","Check if the PETSHARE_BASE Supabase project is active and the edge function is deployed.","If rate-limited, reduce sync frequency or add backoff.","Use source: 'hatchery' as a fallback if only PetShare is down."],"exampleFix":"// before — single fetch with no retry\nconst resp = await fetch(url);\nif (!resp.ok) throw new Error(`petshare list page ${page} failed: ${resp.status}`);\n\n// after — retry with backoff\nfor (let attempt = 0; attempt < 3; attempt++) {\n  const resp = await fetch(url);\n  if (resp.ok) break;\n  if (attempt === 2 || resp.status < 500) {\n    throw new Error(`petshare list page ${page} failed: ${resp.status} ${resp.statusText}`);\n  }\n  await new Promise(r => setTimeout(r, 1000 * (attempt + 1)));\n}","handlingStrategy":"retry","validationCode":"// Before syncing, probe PetShare availability with a lightweight HEAD request.\nasync function isPetShareReachable(): Promise<boolean> {\n  try {\n    const resp = await fetch(`${PETSHARE_BASE}/api/pets?page=1&pageSize=1`, {\n      method: 'HEAD',\n      signal: AbortSignal.timeout(5_000),\n    });\n    return resp.ok;\n  } catch {\n    return false;\n  }\n}","typeGuard":"export function isPetShareListError(error: unknown): boolean {\n  return error instanceof Error && /petshare list page \\d+ failed/.test(error.message);\n}","tryCatchPattern":"try {\n  const tasks = await listPetSharePets(limit);\n} catch (error) {\n  if (error instanceof Error && error.message.includes('petshare list page')) {\n    // Transient — surface as partial failure, try hatchery instead\n    syncResult.errors.push(error.message);\n    const hatcheryTasks = await listHatcheryPets(limit);\n    tasks.push(...hatcheryTasks);\n  } else throw error;\n}","preventionTips":["Treat PetShare as a best-effort external dependency — always provide a fallback source.","Retry with exponential backoff for 5xx responses.","Surface partial failures in the sync result so the user knows which catalog failed.","Cache the last successful sync to serve stale data during outages."],"tags":["network","external-api","community-pets","transient","supabase"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}