toeverything/AFFiNE · warning · Error
Failed to read image size
Error message
Failed to read image size
What it means
Thrown by buildPropsWith (image block utils) when readImageSize(file) returns width*height === 0. readImageSize creates an Image from the file via createObjectURL and resolves {0,0} when the image fails to load (onerror) or the file type is not image/*. The throw aborts insertion; a user-facing toast 'Failed to read image size, please try another image' is shown first. A plain Error, not a BlockSuiteError.
Source
Thrown at blocksuite/affine/blocks/image/src/utils.ts:239
if (exceeded) {
const size = formatSize(maxFileSize);
toast(std.host, `You can only upload files less than ${size}`);
}
return exceeded;
}
async function buildPropsWith(std: BlockStdScope, file: File) {
const { size } = file;
const [imageSize, sourceId] = await Promise.all([
readImageSize(file),
std.store.blobSync.set(file),
]);
if (!(imageSize.width * imageSize.height)) {
toast(std.host, 'Failed to read image size, please try another image');
throw new Error('Failed to read image size');
}
return { size, sourceId, ...imageSize } satisfies Partial<ImageBlockProps>;
}
export async function addSiblingImageBlocks(
std: BlockStdScope,
files: File[],
targetModel: BlockModel,
placement: 'after' | 'before' = 'after'
) {
files = files.filter(file => file.type.startsWith('image/'));
if (!files.length) return [];
if (hasExceeded(std, files)) return [];
const flavour = ImageBlockSchema.model.flavour;
View on GitHub (pinned to 26c515e050)
Solutions
- Pre-validate files before addImageBlocks: check size > 0 and type starts with image/, and for raster types attempt a decode check (createImageBitmap) before insertion.
- Catch the error at the call site of addSiblingImageBlocks/addImageBlocks and skip the offending file, continuing with the rest.
- Advise the user (the built-in toast already does) to try another image; strip non-decodable files from the drop/paste batch upstream.
Example fix
// before
await addSiblingImageBlocks(std, files, targetModel);
// after
const decodable = await Promise.all(
files.map(async f => {
try { await createImageBitmap(f); return f; } catch { return null; }
})
);
await addSiblingImageBlocks(std, decodable.filter(Boolean), targetModel); Defensive patterns
Strategy: validation
Validate before calling
async function isDecodable(file) {
if (!file.type.startsWith('image/') || file.size === 0) return false;
try { await createImageBitmap(file); return true; } catch { return false; }
} Type guard
function isPlausibleImage(file) {
return file.type.startsWith('image/') && file.size > 0;
} Try / catch
try { await addSiblingImageBlocks(std, files, targetModel); }
catch (e) {
if (/read image size/i.test(e?.message ?? '')) {
// skip bad files, retry with the rest
} else throw e;
} Prevention
- Filter files by type and non-zero size before insertion.
- For raster types, pre-decode with createImageBitmap to reject corrupt files early.
- Strip SVGs without intrinsic dimensions or normalise them before upload.
When it happens
Trigger: addSiblingImageBlocks or addImageBlocks processing a File whose MIME is image/* (passes the filter) but the bytes are corrupt, truncated, zero-byte, or an unsupported/decodable format, so the browser's Image decoder fires onerror. Also when the file is a SVG (some browsers report 0x0 for object-URL SVGs without intrinsic dimensions).
Common situations: Uploading a corrupt or partially-downloaded image; a file with a spoofed image/* MIME type that is not actually decodable; SVG without width/height; an empty 0-byte file that passed the earlier filter; very large images that fail to decode under memory pressure.
Related errors
- blob_invalid
- image_format_not_supported
- ErrorCode.DefaultRuntimeError
- Invalid key for: ${key}
- SchemaValidateError
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/52d88a936da8362e.
Report an issue: GitHub.