nexu-io/open-design · warning · Error
${task.folder}: spritesheet too small (${bytes.length} bytes
Error message
${task.folder}: spritesheet too small (${bytes.length} bytes) What it means
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.
Source
Thrown at apps/daemon/src/community-pets-sync.ts:206
const ab = await resp.arrayBuffer();
return Buffer.from(ab);
}
async function writePet(
task: PetTask,
outRoot: string,
force: boolean,
): Promise<'wrote' | 'skipped'> {
const dir = path.join(outRoot, task.folder);
const sheetPath = path.join(dir, `spritesheet.${task.spritesheetExt}`);
const manifestPath = path.join(dir, 'pet.json');
if (!force && (await pathExists(sheetPath)) && (await pathExists(manifestPath))) {
return 'skipped';
}
await mkdir(dir, { recursive: true });
const bytes = await downloadBinary(task.spritesheetUrl);
if (bytes.length < 16) {
throw new Error(`${task.folder}: spritesheet too small (${bytes.length} bytes)`);
}
// Reject HTML error pages dressed as `.webp` so the UI doesn't end up
// adopting a pet whose sprite is `<!doctype html>`.
const head = bytes.subarray(0, 12);
const isWebp = head.toString('ascii', 0, 4) === 'RIFF' && head.toString('ascii', 8, 12) === 'WEBP';
const isPng = head.toString('hex', 0, 8) === '89504e470d0a1a0a';
const isGif = head.toString('ascii', 0, 6) === 'GIF87a' || head.toString('ascii', 0, 6) === 'GIF89a';
if (!isWebp && !isPng && !isGif) {
throw new Error(`${task.folder}: spritesheet is not webp/png/gif`);
}
await writeFile(sheetPath, bytes);
await writeFile(manifestPath, JSON.stringify(task.manifest, null, 2) + '\n', 'utf8');
return 'wrote';
}
async function runPool<T, R>(
items: T[],
concurrency: number,View on GitHub (pinned to 5be4028344)
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.
Defensive patterns
Strategy: validation
Validate before calling
// After download, validate size before writing.
function validateSpritesheetSize(bytes: Buffer, folder: string): void {
const MIN_SIZE = 16;
if (bytes.length < MIN_SIZE) {
throw new Error(
`${folder}: spritesheet too small (${bytes.length} bytes). ` +
`The URL likely returned an error page or empty response.`
);
}
}
// Use before writePet's internal check, or to pre-filter tasks
const bytes = await downloadBinary(url);
validateSpritesheetSize(bytes, folder); Type guard
export function isSpritesheetTooSmallError(error: unknown): boolean {
return error instanceof Error && error.message.includes('spritesheet too small');
} Try / catch
try {
await writePet(task, outRoot, force);
} catch (error) {
if (error instanceof Error && error.message.includes('spritesheet too small')) {
// Skip this pet — the asset is broken on the remote end
syncResult.failed++;
syncResult.errors.push(error.message);
continue; // move to next pet
} else throw error;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ${task.folder}: spritesheet is not webp/png/gif
- download ${url} failed: ${resp.status} ${resp.statusText}
- comment note required
- zip entry size mismatch: ${relPath}
- invalid zip central directory
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/eab573d205bb01e4.
Report an issue: GitHub.