4 Errors
dxtr edited this page 2026-10-18 16:42:17 +02:00

Errors

Every error cl-vk signals is a vk-error, and each says what went wrong and what to do about it. They fall into four groups, beside the type-error of an argument of the wrong type.

error
├─ vk-error
│  ├─ result-error              a Vulkan command failed
│  │  ├─ out-of-date-error
│  │  ├─ surface-lost-error
│  │  └─ device-lost-error
│  ├─ unsupported-error         the system cannot do what was asked
│  ├─ struct-value-error        a struct field got a value it cannot hold
│  ├─ destroyed-object-error    an object was used after being destroyed
│  ├─ live-children-error       an object was destroyed before its children
│  └─ (vk-error itself)         misuse no other condition names
├─ type-error                   an argument of the wrong type
└─ vk-ffi:command-unavailable   a raw command the instance or device lacks

A Vulkan command failed: result-error

Vulkan commands return a VkResult; a negative one is a failure. cl-vk checks the result of every command it calls, and check does the same for raw commands you call. The error names the command and explains the result:

vkQueueSubmit failed with VK_ERROR_DEVICE_LOST: the device was lost, through a
driver error, a GPU reset or a hang

result-error-result returns the result as a keyword, such as :error-out-of-device-memory, and result-error-command the command's name. Three results callers commonly handle have their own subtypes:

Condition Result What to do
out-of-date-error VK_ERROR_OUT_OF_DATE_KHR The swapchain no longer matches its surface: recreate it.
surface-lost-error VK_ERROR_SURFACE_LOST_KHR The window is gone: destroy the swapchain and the surface.
device-lost-error VK_ERROR_DEVICE_LOST Destroy everything made from the device, and the device, then start again.

acquire-next-image and present do not signal for out-of-date swapchains: that is part of normal operation, so they return :out-of-date as a status instead. A raw vkAcquireNextImageKHR or vkQueuePresentKHR passed to check does signal out-of-date-error.

Other common results:

Result Meaning
:error-out-of-host-memory A host memory allocation failed.
:error-out-of-device-memory A device memory allocation failed: free GPU memory or use less.
:error-initialization-failed The driver could not initialize the object.
:error-incompatible-driver No driver supports the Vulkan version asked for.
:error-format-not-supported The format is not supported for this use.
:error-native-window-in-use-khr The window already has a surface.
:error-validation-failed A validation layer found an invalid use of the API.

Results that report success are returned by check as keywords: :success, :suboptimal-khr, :timeout, :not-ready, :incomplete and so on.

The system cannot do it: unsupported-error

Creating an instance, device or swapchain checks the request against what is installed first. Anything missing signals an unsupported-error before any Vulkan object is created, so there is nothing to clean up:

The instance extension VK_KHR_no_such_thing is not available.
AVAILABLE-INSTANCE-EXTENSIONS lists the supported ones.

The layer VK_LAYER_KHRONOS_validation is not available.  Install the Vulkan
validation layers (on Debian and Ubuntu, the package vulkan-validationlayers),
or create the instance without :VALIDATION.

#<PHYSICAL-DEVICE "llvmpipe ..."> does not support these features:
physical-device-ray-tracing-motion-blur-features-nv :RAY-TRACING-MOTION-BLUR.

The surface does not support the present mode :IMMEDIATE.  It supports :MAILBOX, :FIFO.

A swapchain needs the device extension VK_KHR_swapchain; create the device with
it in :EXTENSIONS.

unsupported-error-missing lists what is missing, for a program that falls back:

(handler-case (vk:create-swapchain device surface :present-mode :mailbox)
  (vk:unsupported-error ()
    (vk:create-swapchain device surface :present-mode :fifo)))

It is signalled for: a Vulkan version the loader does not support; a missing layer, instance extension or device extension; a missing feature; a queue family that does not exist or has too few queues; a format, present mode, image count or image usage the surface does not support; and a surface no queue of the device can present to.

A struct field got a wrong value: struct-value-error

Filling a struct with a value of the wrong kind signals a struct-value-error naming the struct, the field, the value and what the field takes:

"linear" is not a valid value for the field :MAG-FILTER of VKS:SAMPLER-CREATE-INFO:
expected a keyword or an integer.

(1) is not a valid value for the field :WAIT-SEMAPHORE-INFOS of VKS:SUBMIT-INFO2:
expected a foreign pointer: SET-STRUCT does not allocate; use WITH-STRUCTS for lists.

Mistakes the macros can see when they expand (an unknown field, a constant keyword that names no value, a field given twice) are vk-error conditions at compile time instead, listing what is valid. See Structs.

Object lifetimes: destroyed-object-error, live-children-error

Using an object after it was destroyed, or destroying it twice, signals destroyed-object-error instead of passing a dangling handle to the driver:

#<DEVICE "llvmpipe (LLVM 21.1.8, 256 bits)" (destroyed)> has been destroyed and
cannot be used.

Destroying an object while objects made from it are alive signals live-children-error and destroys nothing:

#<DEVICE "llvmpipe (LLVM 21.1.8, 256 bits)"> cannot be destroyed while these
objects made from it are alive: #<FENCE>.  Destroy them first.

live-children-error-children lists them. See Lifetimes and Threads.

A raw command is not available: command-unavailable

Calling a raw command that the instance or device does not provide, because the API version or extension it belongs to was not enabled, signals vk-ffi:command-unavailable instead of crashing:

vkCmdDrawMeshTasksEXT is not available: the device did not provide it.  Enable
the API version or extension that provides it when creating the device.

vk-ffi:command-available-p asks first. This condition belongs to the raw layer and is not a vk-error. See Raw FFI Layer.

Malformed arguments

Wrong argument types signal type-error. Every public function declares the types of its arguments, which SBCL checks on entry, so a constant of the wrong type is already a warning when the call is compiled; the elements of a list are checked in the body: a queue request that is not (FAMILY COUNT), or a feature request that is not (STRUCT FIELD ...), among them. In the raw layer, bit-field and (setf bit-field) do the same for a struct without bit-fields, a field it has not or a value that does not fit, and the error's expected type lists what is valid.

Misuse that types cannot express signals a vk-error whose message says what to write instead: a queue family given twice to create-device, a field that is not a feature, device-queue for a queue the device was not created with, acquire-next-image with neither a semaphore nor a fence, create-swapchain without an :extent for a surface that has no size, or a struct name that names no Vulkan struct:

The queue family 0 is asked for twice; ask once, for all its queues.

ACQUIRE-NEXT-IMAGE needs a :SEMAPHORE or a :FENCE to signal when the image is ready.

Validation messages are not conditions

What the validation layer reports is queued, not signalled: Vulkan calls the debug messenger from inside the driver, where unwinding is not allowed. Read the messages with take-debug-messages; see Instances and Validation.