{"record":{"id":"eab573d205bb01e4","repo":"nexu-io/open-design","slug":"task-folder-spritesheet-too-small-bytes-len","errorCode":null,"errorMessage":"${task.folder}: spritesheet too small (${bytes.length} bytes)","messagePattern":"(.+?): spritesheet too small \\((.+?) bytes\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/daemon/src/community-pets-sync.ts","lineNumber":206,"sourceCode":"  const ab = await resp.arrayBuffer();\n  return Buffer.from(ab);\n}\n\nasync function writePet(\n  task: PetTask,\n  outRoot: string,\n  force: boolean,\n): Promise<'wrote' | 'skipped'> {\n  const dir = path.join(outRoot, task.folder);\n  const sheetPath = path.join(dir, `spritesheet.${task.spritesheetExt}`);\n  const manifestPath = path.join(dir, 'pet.json');\n  if (!force && (await pathExists(sheetPath)) && (await pathExists(manifestPath))) {\n    return 'skipped';\n  }\n  await mkdir(dir, { recursive: true });\n  const bytes = await downloadBinary(task.spritesheetUrl);\n  if (bytes.length < 16) {\n    throw new Error(`${task.folder}: spritesheet too small (${bytes.length} bytes)`);\n  }\n  // Reject HTML error pages dressed as `.webp` so the UI doesn't end up\n  // adopting a pet whose sprite is `<!doctype html>`.\n  const head = bytes.subarray(0, 12);\n  const isWebp = head.toString('ascii', 0, 4) === 'RIFF' && head.toString('ascii', 8, 12) === 'WEBP';\n  const isPng = head.toString('hex', 0, 8) === '89504e470d0a1a0a';\n  const isGif = head.toString('ascii', 0, 6) === 'GIF87a' || head.toString('ascii', 0, 6) === 'GIF89a';\n  if (!isWebp && !isPng && !isGif) {\n    throw new Error(`${task.folder}: spritesheet is not webp/png/gif`);\n  }\n  await writeFile(sheetPath, bytes);\n  await writeFile(manifestPath, JSON.stringify(task.manifest, null, 2) + '\\n', 'utf8');\n  return 'wrote';\n}\n\nasync function runPool<T, R>(\n  items: T[],\n  concurrency: number,","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/community-pets-sync.ts#L188-L224","documentation":"Thrown by writePet when the downloaded spritesheet binary is smaller than 16 bytes. This is a sanity guard: no valid webp/png/gif image is under 16 bytes. A tiny payload almost always means the URL returned an error page, a redirect HTML snippet, or an empty response that passed the resp.ok check but isn't a real image. The error includes the folder name and byte count.","triggerScenarios":"downloadBinary succeeds (HTTP 200) but the response body is tiny — e.g., a 0-byte response, a truncated file, or a short HTML error string. The bytes.length < 16 check at line 207 fires.","commonSituations":"The CDN returns HTTP 200 with an empty or error body. A proxy strips the image content. The spritesheet URL points to a placeholder/redirect page. A Supabase storage bucket returns a tiny JSON error with 200 status.","solutions":["Manually open the spritesheetUrl in a browser to verify it serves a real image.","Check if the hosting service changed its response format or the asset was corrupted.","Retry the download — transient partial responses sometimes resolve.","If the asset is permanently broken, mark the pet as failed and exclude it from future syncs."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// After download, validate size before writing.\nfunction validateSpritesheetSize(bytes: Buffer, folder: string): void {\n  const MIN_SIZE = 16;\n  if (bytes.length < MIN_SIZE) {\n    throw new Error(\n      `${folder}: spritesheet too small (${bytes.length} bytes). ` +\n      `The URL likely returned an error page or empty response.`\n    );\n  }\n}\n\n// Use before writePet's internal check, or to pre-filter tasks\nconst bytes = await downloadBinary(url);\nvalidateSpritesheetSize(bytes, folder);","typeGuard":"export function isSpritesheetTooSmallError(error: unknown): boolean {\n  return error instanceof Error && error.message.includes('spritesheet too small');\n}","tryCatchPattern":"try {\n  await writePet(task, outRoot, force);\n} catch (error) {\n  if (error instanceof Error && error.message.includes('spritesheet too small')) {\n    // Skip this pet — the asset is broken on the remote end\n    syncResult.failed++;\n    syncResult.errors.push(error.message);\n    continue; // move to next pet\n  } else throw error;\n}","preventionTips":["Treat small responses as broken assets — don't retry indefinitely.","Surface the failure per-pet so the rest of the sync continues.","Check the URL manually if the same pet consistently fails.","Log the byte count to distinguish between empty (0 bytes) and truncated responses."],"tags":["community-pets","validation","data-integrity","download"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}