{"record":{"id":"a265ccd06caf5fca","repo":"microsoft/garnet","slug":"failed-to-schedule-async-io-handle","errorCode":null,"errorMessage":"Failed to schedule async IO: {}, handle {}","messagePattern":"Failed to schedule async IO: (.+?), handle (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cc/src/device/file_windows.cc","lineNumber":385,"sourceCode":"\n  new(io_context.get()) QueueIoHandler::IoCallbackContext(offset, caller_context_copy,\n      callback);\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      std::stringstream ss;\n      ss << \"Failed to schedule async IO: \" << FormatWin32AndHRESULT(win32_result) <<\n         \", handle \" << std::to_string((uint64_t)file_handle_);\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\n#undef DCHECK_ALIGNMENT\n\n}\n} // namespace FASTER::environment","sourceCodeStart":367,"sourceCodeEnd":396,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cc/src/device/file_windows.cc#L367-L396","documentation":"Same failure class as [344] but in QueueFile::ScheduleOperation (QueueIoHandler / manual IOCP variant rather than the threadpool-I/O object). ReadFile/WriteFile fails with a Win32 code other than ERROR_IO_PENDING; the message appends the raw file_handle_ value for correlation. Returns Status::IOError. There is no CancelThreadpoolIo here (this path uses an IOCP + GetQueuedCompletionStatus loop, not a TP_IO), so the message omits that step.","triggerScenarios":"QueueIoHandler-based device Read/Write on an overlapped handle fails synchronously. Same root causes as [344] — unbuffered-I/O misalignment (DCHECK_ALIGNMENT only in debug), invalid/closed handle, device/network removal, disk full on write. The handle suffix in the message lets you map the failure back to the specific segment file.","commonSituations":"Using the QueueFile/QueueIoHandler device variant with non-sector-aligned buffers or sizes; IO attempted after Close(); removable/network storage dropping; concurrent reopen of the same segment file. Distinct from [344] only by which device implementation is in use — this variant is selected when you construct the queue-based handler.","solutions":["Validate offset/length/buffer against device alignment (file.device_alignment()) before every Read/Write on unbuffered devices; use aligned allocators for buffers.","Match the handle value printed in the message to your open-file table to identify which segment/operation failed.","Block IO after Close()/Dispose with a disposed guard and a valid file_handle_ check.","For transient network/removable-media codes, retry with backoff; for ERROR_INVALID_PARAMETER (87) fix alignment rather than retrying."],"exampleFix":"// before:\n// file.Write(offset, length, src, ctx, cb);   // misaligned on QueueFile unbuffered device\n// after:\nsize_t sector = file.device_alignment();\nif (offset % sector != 0 || length % sector != 0 ||\n    reinterpret_cast<uintptr_t>(src) % sector != 0) {\n    return Status::IOError;   // fail fast with a clear cause instead of Win32(87)\n}\nfile.Write(offset, length, src, ctx, cb);","handlingStrategy":"validation","validationCode":"// Same alignment/handle checks for the QueueIoHandler device variant.\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;\nif (file.handle() == INVALID_HANDLE_VALUE) return Status::IOError;\n// Correlate failures via the handle value printed in the stderr message.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Align offset/length/buffer to device_alignment() for the QueueFile/QueueIoHandler device; allocate buffers with aligned allocators.","Match the printed handle value to your open-file table to find the failing segment.","Block IO after Close()/Dispose with a disposed guard and a valid-handle check.","Retry only transient (network/removable-media) codes; fix alignment rather than retrying ERROR_INVALID_PARAMETER (87)."],"tags":["tsavorite","cpp","windows","io","win32","iocompletionport","alignment"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}