{"record":{"id":"52d88a936da8362e","repo":"toeverything/AFFiNE","slug":"failed-to-read-image-size","errorCode":null,"errorMessage":"Failed to read image size","messagePattern":"Failed to read image size","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"blocksuite/affine/blocks/image/src/utils.ts","lineNumber":239,"sourceCode":"\n  if (exceeded) {\n    const size = formatSize(maxFileSize);\n    toast(std.host, `You can only upload files less than ${size}`);\n  }\n\n  return exceeded;\n}\n\nasync function buildPropsWith(std: BlockStdScope, file: File) {\n  const { size } = file;\n  const [imageSize, sourceId] = await Promise.all([\n    readImageSize(file),\n    std.store.blobSync.set(file),\n  ]);\n\n  if (!(imageSize.width * imageSize.height)) {\n    toast(std.host, 'Failed to read image size, please try another image');\n    throw new Error('Failed to read image size');\n  }\n\n  return { size, sourceId, ...imageSize } satisfies Partial<ImageBlockProps>;\n}\n\nexport async function addSiblingImageBlocks(\n  std: BlockStdScope,\n  files: File[],\n  targetModel: BlockModel,\n  placement: 'after' | 'before' = 'after'\n) {\n  files = files.filter(file => file.type.startsWith('image/'));\n  if (!files.length) return [];\n\n  if (hasExceeded(std, files)) return [];\n\n  const flavour = ImageBlockSchema.model.flavour;\n","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/blocksuite/affine/blocks/image/src/utils.ts#L221-L257","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nawait addSiblingImageBlocks(std, files, targetModel);\n\n// after\nconst decodable = await Promise.all(\n  files.map(async f => {\n    try { await createImageBitmap(f); return f; } catch { return null; }\n  })\n);\nawait addSiblingImageBlocks(std, decodable.filter(Boolean), targetModel);","handlingStrategy":"validation","validationCode":"async function isDecodable(file) {\n  if (!file.type.startsWith('image/') || file.size === 0) return false;\n  try { await createImageBitmap(file); return true; } catch { return false; }\n}","typeGuard":"function isPlausibleImage(file) {\n  return file.type.startsWith('image/') && file.size > 0;\n}","tryCatchPattern":"try { await addSiblingImageBlocks(std, files, targetModel); }\ncatch (e) {\n  if (/read image size/i.test(e?.message ?? '')) {\n    // skip bad files, retry with the rest\n  } else throw e;\n}","preventionTips":["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."],"tags":["image","upload","decode-error","validation"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}