{"record":{"id":"68925d4138032b68","repo":"denoland/deno","slug":"ebadf","errorCode":"EBADF","errorMessage":"file closed","messagePattern":"file closed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/fs/handle.ts","lineNumber":618,"sourceCode":"      lazyFs().write(\n        rid,\n        str,\n        position,\n        encoding,\n        (err: Error, bytesWritten: number, buffer: Buffer) => {\n          if (err) reject(err);\n          else resolve({ buffer, bytesWritten });\n        },\n      );\n    });\n  }\n}\n\nfunction assertNotClosed(rid: number, syscall: string) {\n  if (rid === -1) {\n    const err = new Error(\"file closed\");\n    throw ObjectAssign(err, {\n      code: \"EBADF\",\n      syscall,\n    });\n  }\n}\n\ntype FileHandleFn<P, R> = (...args: [number, ...P[]]) => Promise<R>;\n\nasync function fsCall<P, R, T extends FileHandleFn<P, R>>(\n  fn: T,\n  fnName: string,\n  handle: FileHandle,\n  ...args: P[]\n): Promise<R> {\n  assert(\n    handle[kRefs] !== undefined,\n    \"handle must be an instance of FileHandle\",\n  );\n  assertNotClosed(handle.fd, fnName);","sourceCodeStart":600,"sourceCodeEnd":636,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/fs/handle.ts#L600-L636","documentation":"Every FileHandle method in Deno's node:fs/promises polyfill routes through fsCall, which first calls assertNotClosed (handle.ts:614-622). After close(), the handle's backing rid becomes -1, and any subsequent read/write/stat/truncate/appendFiles call throws Error('file closed') with code EBADF and syscall set to the method name. It mirrors Node's EBADF for operating on an already-closed descriptor; note handle.fd also reports -1 (handle.ts:160-162, 395).","triggerScenarios":"await fh.close() followed by fh.read(...); fire-and-forget writes racing an explicit close; retry wrappers that close on the first error and then reuse the handle; a second read after readFile helpers already closed the handle (promises.ts handleFdClose).","commonSituations":"Cleanup in finally{} running while queued writes are still in flight; queue consumers that close after the first item and keep processing; passing FileHandle across module boundaries with no single owner; double-close followed by use.","solutions":["Serialize usage: await every operation before calling close(), ideally open → work → close within one function scope.","Before reusing a possibly-closed handle, guard with fh.fd !== -1 (the polyfill reports -1 after close).","Restructure to per-task handles: open the file again instead of sharing one long-lived FileHandle.","Search for close() calls in error/abort paths — an early close is the usual culprit."],"exampleFix":"// before\nconst fh = await fs.promises.open('log.txt', 'a');\nawait fh.close();\nawait fh.write('line\\n'); // EBADF: file closed, syscall 'write'\n\n// after\nconst fh = await fs.promises.open('log.txt', 'a');\ntry {\n  await fh.write('line\\n');\n} finally {\n  await fh.close();\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"/** True while the FileHandle is still usable (Deno polyfill sets fd to -1 after close). */\nfunction isFileOpen(fh) {\n  return typeof fh.fd === 'number' && fh.fd !== -1;\n}","tryCatchPattern":"try {\n  await fh.write(data);\n} catch (err) {\n  if (err.code === 'EBADF' && err.message === 'file closed') {\n    fh = await fs.promises.open(path, flags); // reopen and retry once\n    await fh.write(data);\n  } else throw err;\n}","preventionTips":["Own the lifecycle: open, use, and close the handle in one function scope with try/finally","Await every operation before close() — no fire-and-forget writes on a shared handle","Guard reuse with fh.fd !== -1","Audit error and abort paths for early close() calls"],"tags":["deno","node-compat","fs","filehandle","ebadf","use-after-close"],"backgroundTag":"use-after-close","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}