{"record":{"id":"76ade52ef7eae869","repo":"denoland/deno","slug":"invalidstateerror-76ade5","errorCode":"InvalidStateError","errorMessage":"Invalid FileReader state","messagePattern":"Invalid FileReader state","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"error","filePath":"ext/web/10_filereader.js","lineNumber":76,"sourceCode":"\nclass FileReader extends EventTarget {\n  /** @type {\"empty\" | \"loading\" | \"done\"} */\n  [state] = \"empty\";\n  /** @type {null | string | ArrayBuffer} */\n  [result] = null;\n  /** @type {null | DOMException} */\n  [error] = null;\n  /** @type {null | {aborted: boolean}} */\n  [aborted] = null;\n\n  /**\n   * @param {Blob} blob\n   * @param {{kind: \"ArrayBuffer\" | \"Text\" | \"DataUrl\" | \"BinaryString\", encoding?: string}} readtype\n   */\n  #readOperation(blob, readtype) {\n    // 1. If fr's state is \"loading\", throw an InvalidStateError DOMException.\n    if (this[state] === \"loading\") {\n      throw new DOMException(\n        \"Invalid FileReader state\",\n        \"InvalidStateError\",\n      );\n    }\n    // 2. Set fr's state to \"loading\".\n    this[state] = \"loading\";\n    // 3. Set fr's result to null.\n    this[result] = null;\n    // 4. Set fr's error to null.\n    this[error] = null;\n\n    // We set this[aborted] to a new object, and keep track of it in a\n    // separate variable, so if a new read operation starts while there are\n    // remaining tasks from a previous aborted operation, the new operation\n    // will run while the tasks from the previous one are still aborted.\n    const abortedState = this[aborted] = { aborted: false };\n\n    // 5. Let stream be the result of calling get stream on blob.","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/web/10_filereader.js#L58-L94","documentation":"InvalidStateError DOMException thrown at the top of FileReader's #readOperation (ext/web/10_filereader.js), implementing the spec's step 1: if the FileReader's state is already \"loading\" — a read is in flight — starting another read is rejected. Each FileReader performs one read at a time; readiness is observable via the readyState property (EMPTY=0, LOADING=1, DONE=2).","triggerScenarios":"Calling readAsText/readAsArrayBuffer/readAsDataURL/readAsBinaryString while a previous readAs* on the same FileReader has not fired loadend yet — e.g. issuing reads in a loop without awaiting completion, or firing a second read from a click handler while the first is still running.","commonSituations":"Loops that read a list of files with one shared FileReader; UI handlers that re-trigger on double-click before the first read finishes; code migrated from callback style where each read assumed a fresh reader.","solutions":["Await completion before the next read: listen for loadend (or wrap the read in a Promise resolving on loadend/onerror/onabort) and chain reads sequentially.","Use a new FileReader per read — they are cheap and stateless between reads.","Prefer the promise APIs when possible: await blob.text(), blob.arrayBuffer(), or blob.data URL equivalents instead of FileReader.","Guard with if (reader.readyState !== FileReader.LOADING) before calling readAs*."],"exampleFix":"// before\nfor (const f of files) reader.readAsText(f); // 2nd call throws: still loading\n\n// after\nfunction read(file) {\n  return new Promise((res, rej) => {\n    reader.onload = () => res(reader.result);\n    reader.onerror = () => rej(reader.error);\n    reader.readAsText(file);\n  });\n}\nfor (const f of files) await read(f); // sequential, always EMPTY when started","handlingStrategy":"validation","validationCode":"// readyState: EMPTY(0) LOADING(1) DONE(2) — only start when EMPTY/DONE.\nfunction readText(reader, blob) {\n  if (reader.readyState === FileReader.LOADING) {\n    return Promise.reject(new Error('FileReader busy'));\n  }\n  return new Promise((res, rej) => {\n    reader.onload = () => res(reader.result);\n    reader.onerror = () => rej(reader.error);\n    reader.readAsText(blob);\n  });\n}","typeGuard":"function fileReaderIdle(reader) {\n  return reader.readyState !== FileReader.LOADING;\n}","tryCatchPattern":"try {\n  reader.readAsText(blob);\n} catch (err) {\n  if (err instanceof DOMException && err.name === 'InvalidStateError') {\n    return queueReadAfterLoadend(reader, blob); // retry once idle\n  }\n  throw err;\n}","preventionTips":["Use one FileReader per read, or serialize reads behind a loadend event.","Prefer await blob.text()/blob.arrayBuffer() over FileReader in Deno.","Wrap readAs* in a promise that resolves on loadend before issuing the next read."],"tags":["filereader","dom","async","file-api"],"backgroundTag":"filereader-busy","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}