nexu-io/open-design · warning · Error
hatchery list failed: ${resp.status} ${resp.statusText}
Error message
hatchery list failed: ${resp.status} ${resp.statusText} What it means
Thrown by listHatcheryPets when the HTTP GET to the Hatchery catalog API (HATCHERY_LIST at https://j20.nz/hatchery/api/pets.json) returns a non-2xx status. Unlike PetShare, the Hatchery API is a single flat JSON endpoint with no pagination. The error message includes the HTTP status and statusText for diagnostics.
Source
Thrown at apps/daemon/src/community-pets-sync.ts:154
tags: pet.tags ?? [],
source: 'codex-pet-share',
sourceUrl: `https://codex-pet-share.pages.dev/#/pets/${encodeURIComponent(pet.id ?? '')}`,
},
spritesheetUrl,
spritesheetExt: ext,
});
if (limit && tasks.length >= limit) return tasks;
}
if (page >= (data.totalPages ?? 1)) break;
page++;
}
return tasks;
}
async function listHatcheryPets(limit: number | null): Promise<PetTask[]> {
const resp = await fetch(HATCHERY_LIST);
if (!resp.ok) {
throw new Error(`hatchery list failed: ${resp.status} ${resp.statusText}`);
}
const data = (await resp.json()) as HatcheryListResponse;
const tasks: PetTask[] = [];
for (const pet of data.pets ?? []) {
const folder = sanitizeFolder(pet.petManifestId || pet.id);
if (!folder) continue;
if (!pet.spritesheetUrl) continue;
tasks.push({
source: 'hatchery',
folder,
manifest: {
id: pet.petManifestId || pet.id,
displayName: pet.displayName,
description: pet.description ?? '',
spritesheetPath: 'spritesheet.webp',
author: pet.authorLabel,
authorXUrl: pet.authorXUrl,
source: 'j20-hatchery',View on GitHub (pinned to 5be4028344)
Solutions
- Retry the sync after a short delay — Hatchery outages are typically transient.
- Verify https://j20.nz/hatchery/api/pets.json is accessible from the daemon's network.
- Use source: 'petshare' as a fallback if only Hatchery is down.
- If the endpoint has permanently moved, update HATCHERY_LIST constant.
Defensive patterns
Strategy: retry
Validate before calling
// Before syncing, probe Hatchery availability.
async function isHatcheryReachable(): Promise<boolean> {
try {
const resp = await fetch(HATCHERY_LIST, {
method: 'HEAD',
signal: AbortSignal.timeout(5_000),
});
return resp.ok;
} catch {
return false;
}
} Type guard
export function isHatcheryListError(error: unknown): boolean {
return error instanceof Error && error.message.startsWith('hatchery list failed');
} Try / catch
try {
const tasks = await listHatcheryPets(limit);
} catch (error) {
if (error instanceof Error && error.message.startsWith('hatchery list failed')) {
// Transient — surface as partial failure, try petshare instead
syncResult.errors.push(error.message);
const petshareTasks = await listPetSharePets(limit);
tasks.push(...petshareTasks);
} else throw error;
} Prevention
- Treat Hatchery as a best-effort external dependency.
- Retry with exponential backoff for 5xx responses.
- Surface partial failures in the sync result.
- Fall back to PetShare when Hatchery is unavailable.
When it happens
Trigger: Calling the community pets sync (POST /api/codex-pets/sync with source 'hatchery' or 'all') when the j20.nz hatchery endpoint returns 4xx/5xx. The fetch at line 152 gets a response, resp.ok is false, and the error is thrown at line 154.
Common situations: The j20.nz server is down, the /hatchery/api/pets.json route has changed, or a CDN/proxy returns an error. Network connectivity to j20.nz is blocked or timing out.
Related errors
- petshare list page ${page} failed: ${resp.status} ${resp.sta
- download ${url} failed: ${resp.status} ${resp.statusText}
- Could not fetch ${url} — the site may block server-side requ
- invalid brand asset url: ${String(url)}
- blocked non-public brand asset host: ${host}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/7a66c1f9c97b25db.
Report an issue: GitHub.