toeverything/AFFiNE · error · DOMException

NotReadableError

NotReadableError

Error message

File could not be read

What it means

snapshotFile retries file.arrayBuffer() three times for transient NotReadable errors and this DOMException fires only if all attempts failed or the loop exited without bytes, i.e. the File handle stayed unreadable (e.g. file moved/deleted on disk mid-read).

Source

Thrown at blocksuite/affine/shared/src/utils/file/filesys.ts:198

function sleep(ms: number) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

export async function snapshotFile(file: File, relativePath?: string) {
  let bytes: Uint8Array | null = null;
  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      bytes = new Uint8Array(await file.arrayBuffer());
      break;
    } catch (error) {
      if (!isNotReadableError(error) || attempt === 2) {
        throw error;
      }
      await sleep(100 * (attempt + 1));
    }
  }
  if (!bytes) {
    throw new DOMException('File could not be read', 'NotReadableError');
  }
  const snapshot = new File([copyToArrayBuffer(bytes)], file.name, {
    type: file.type,
    lastModified: file.lastModified,
  });
  const path = relativePath ?? file.webkitRelativePath;
  if (path) {
    Object.defineProperty(snapshot, 'webkitRelativePath', {
      value: path,
      writable: false,
    });
  }
  return snapshot;
}

export async function snapshotFiles(files: File[]) {
  const results = await Promise.allSettled(
    files.map(file => snapshotFile(file))

View on GitHub (pinned to b4c8548c09)

Solutions

  1. Verify the File/Blob object is still valid and not already consumed by a previous read; re-obtain the file handle before reading.
  2. Check that the file actually exists and is readable (permissions, path) before calling file.arrayBuffer() or FileReader.readAsArrayBuffer.
  3. Handle the DOMException from the underlying FileReader by surfacing its message to the user so they can retry or pick a different file.

Example fix

async function readFile(file: File): Promise<ArrayBuffer> {
  if (!file || file.size === 0) throw new Error('Empty file selected');
  try {
    return await file.arrayBuffer();
  } catch (e) {
    throw new Error(`File could not be read: ${(e as Error).message}`);
  }
}
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at blocksuite/affine/shared/src/utils/file/filesys.ts:198 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/bd0459e5b3e9c1ad. Report an issue: GitHub.