{"record":{"id":"14c2e191a09b9db3","repo":"microsoft/garnet","slug":"failed-to-schedule-async-io","errorCode":null,"errorMessage":"Failed to schedule async IO: {}","messagePattern":"Failed to schedule async IO: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cc/src/device/file_windows.cc","lineNumber":268,"sourceCode":"      callback);\n\n  ::StartThreadpoolIo(io_object_);\n\n  bool success = FALSE;\n  if(FileOperationType::Read == operationType) {\n    success = ::ReadFile(file_handle_, buffer, length, nullptr, &io_context->parent_overlapped);\n  } else {\n    success = ::WriteFile(file_handle_, buffer, length, nullptr, &io_context->parent_overlapped);\n  }\n  if(!success) {\n    DWORD win32_result = ::GetLastError();\n    // Any error other than ERROR_IO_PENDING means the IO failed. Otherwise it will finish\n    // asynchronously on the threadpool\n    if(ERROR_IO_PENDING != win32_result) {\n      ::CancelThreadpoolIo(io_object_);\n      std::stringstream ss;\n      ss << \"Failed to schedule async IO: \" << FormatWin32AndHRESULT(win32_result);\n      fprintf(stderr, \"%s\\n\", ss.str().c_str());\n      return Status::IOError;\n    }\n  }\n  io_context.release();\n  return Status::Ok;\n}\n\nbool QueueIoHandler::TryComplete() {\n  DWORD bytes_transferred;\n  ULONG_PTR completion_key;\n  LPOVERLAPPED overlapped = NULL;\n  bool succeeded = ::GetQueuedCompletionStatus(io_completion_port_, &bytes_transferred,\n                   &completion_key, &overlapped, 0);\n  if(overlapped) {\n    Status return_status;\n    if(!succeeded) {\n      return_status = Status::IOError;\n    } else {","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cc/src/device/file_windows.cc#L250-L286","documentation":"Returned by ThreadPoolFile::ScheduleOperation when ReadFile/WriteFile fails with a Win32 code other than ERROR_IO_PENDING. The handler uses the Win32 threadpool I/O object (StartThreadpoolIo was called first); on failure it calls CancelThreadpoolIo to balance the pending count, formats FormatWin32AndHRESULT(win32_result), prints to stderr, and returns Status::IOError. The io_context is freed (not released) so no completion callback fires.","triggerScenarios":"ReadFile/WriteFile fails synchronously on an overlapped handle. Dominant causes: unbuffered I/O (FILE_FLAG_NO_BUFFERING) with offset/length/buffer not aligned to device sector size (the DCHECK_ALIGNMENT assert is only compiled in _DEBUG, so misalignment slips through in release); an invalid/closed file_handle_; device removed or network share dropped; disk full or quota exceeded on write; handle reopened concurrently.","commonSituations":"Calling Read/Write with a buffer or size not a multiple of the sector size on an unbuffered device; passing an offset/length of 0 or non-sector-multiple; using a stack/unaligned buffer; operating on a device after Close(); removable media ejected mid-run; SMB/network path failure.","solutions":["Enforce sector alignment of offset, length, and buffer for every Read/Write on unbuffered devices — read device alignment from the file (GetDeviceAlignment) and round up; pin buffers via aligned allocation.","Decode the Win32 code in the stderr line: ERROR_INVALID_PARAMETER (87) almost always means misalignment; ERROR_HANDLE_EOF/ERROR_INVALID_HANDLE means handle state; network codes mean transport.","Do not issue IO after Close()/device Dispose; gate IO on a valid file_handle_ and a disposed flag.","For transient transport errors (network shares), retry the operation with backoff; for alignment errors fix the buffer/size, do not retry unchanged."],"exampleFix":"// before:\n// file.Read(offset, length, buffer, ctx, cb);   // length not sector-aligned on unbuffered device\n// after (align to device sector size):\nsize_t sector = file.device_alignment();\nassert(offset % sector == 0 && length % sector == 0 &&\n       reinterpret_cast<uintptr_t>(buffer) % sector == 0);\nfile.Read(offset, length, buffer, ctx, cb);","handlingStrategy":"validation","validationCode":"// Validate alignment before issuing overlapped IO on a (possibly unbuffered) device.\nsize_t sector = file.device_alignment();\nif (offset % sector != 0 || length % sector != 0 ||\n    reinterpret_cast<uintptr_t>(buffer) % sector != 0)\n    return Status::IOError;   // surface misalignment explicitly\nif (file.handle() == INVALID_HANDLE_VALUE) return Status::IOError;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always align offset, length, and buffer to file.device_alignment() for unbuffered devices; use aligned allocators.","Gate IO on a valid file handle and a not-disposed flag; never IO after Close().","Decode the Win32 code: ERROR_INVALID_PARAMETER (87) => alignment; transport codes => network/removable; handle codes => state.","Retry only transient transport errors with backoff; never retry a misalignment failure unchanged."],"tags":["tsavorite","cpp","windows","io","win32","overlapped-io","alignment"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}