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

  1. Retry the sync after a short delay — Hatchery outages are typically transient.
  2. Verify https://j20.nz/hatchery/api/pets.json is accessible from the daemon's network.
  3. Use source: 'petshare' as a fallback if only Hatchery is down.
  4. 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

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


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/7a66c1f9c97b25db. Report an issue: GitHub.