#1615·ggml

vulkan: vkGetDeviceQueue2 returns VK_NULL_HANDLE on older AMD drivers, first vkQueueSubmit aborts

Author: thewh1teagleCreated Sep 4, 2026Updated Sep 4, 2026

What happens

Since the queue handle refactor (vulkan: Refactor vk_queue to use per-instance mutexes and unique handles, llama.cpp #23570, ggml commit 3ca9985), ggml_vk_create_queue fetches every queue through vkGetDeviceQueue2:

cpp
h->queue = device->device.getQueue2(queue_info2);

Some older drivers return VK_NULL_HANDLE from vkGetDeviceQueue2 even for queues they created. The first vkQueueSubmit then fails and the process aborts:

ERROR:             vkQueueSubmit: Invalid queue [VUID-vkQueueSubmit-queue-parameter]

(exit code 0xC0000409 on Windows). Present in v0.22.0 and on master as of 2026-08-30 (d471637). ggml 0.16 used getQueue and worked on the same machine.

Reproduction

AMD Ryzen 5 4500U with Radeon Graphics, Windows 11, AMD driver 27.20.11044.7 (Vulkan 1.2.133, driverVersion 0x800089). Loader 1.3.300.

A minimal program that creates a device with one compute queue (no flags) and fetches it both ways:

device "AMD Radeon(TM) Graphics" api 1.2.133 driver 0x800089
  family 0: count 1 flags GRAPHICS | COMPUTE | TRANSFER | SPARSE_BINDING
  family 1: count 2 flags COMPUTE | TRANSFER | SPARSE_BINDING
  family 2: count 1 flags TRANSFER | SPARSE_BINDING
  family 0: vkGetDeviceQueue -> 0x1f87fb67bc0   vkGetDeviceQueue2 -> 0x0
  empty vkQueueSubmit on queue from vkGetDeviceQueue: SUCCESS

Any ggml Vulkan workload on this machine (here a whisper large-v3-turbo encode) reproduces the abort; GGML_VK_VISIBLE_DEVICES=99 (CPU only) works.

Suggested fix

When has_internally_synchronized_queues is false the queue was created without flags, so vkGetDeviceQueue names the same queue. A null check is enough:

cpp
h->queue = device->device.getQueue2(queue_info2);
if (!h->queue) {
    // Some older drivers (e.g. AMD 20.x on Windows, Vulkan 1.2.133) return a null
    // handle from vkGetDeviceQueue2 even for queues they created.
    h->queue = device->device.getQueue(queue_family_index, queue_index);
}

We are shipping this as a local patch on top of v0.22.0 (vibe-transcribe/sona#50); the standalone test above shows vkGetDeviceQueue returns a working queue on that device, and I will report back once the patched build has run there. Happy to open a PR here or on llama.cpp, whichever you prefer.