6 Errors
dxtr edited this page 2026-10-18 16:19:56 +02:00

Errors

Every condition cl-wl signals is a wl-error. Each one's report says what went wrong in protocol terms, and wl-error-message returns that text. Foreign error codes never escape as integers.

wl-error
├── connection-error        the connection could not be made, or broke
├── compositor-error        the compositor raised a fatal protocol error
├── version-error           a request or bind needs a newer interface version
├── destroyed-proxy-error   a destroyed proxy was used
├── event-error             an event could not be decoded
└── missing-global-error    BIND found no global of the interface

A wrong argument is not a wl-error. Every exported function declares its arguments' types, so SBCL signals a type-error that names the argument before anything happens, and warns at compile time where it can tell. A display passed to destroy-proxy, which takes any proxy but a display, is one:

The value
  #<WL-DISPLAY id 1 v1 {1202F8DDC3}>
is not of type
  (AND CL-WL:PROXY (NOT CL-WL:DISPLAY))
when binding PROXY

wl-error itself is signalled for misuse that has no more specific condition. Examples are setting a handler for an event the interface does not have, an odd number of arguments to set-event-handlers, and a define-request form for a request it cannot define, which is refused when the form is expanded. It is also signalled when the system cannot provide shared memory, when pixels written with write-shared-memory do not fit, when read-keymap cannot map a keymap, when close-fd cannot close a file descriptor, when make-pipe cannot make a pipe, and when receive-text or send-text cannot read or write one, or the text read is not UTF-8. A pipe whose other end closes early, or a timeout, is not an error: receive-text returns nil and send-text false. A borrowed proxy's destructor request, and a cursor theme used after destroy-cursor-theme or destroyed after its display is disconnected, signal wl-error too, as does misuse of the test compositor, such as posting an event on a destroyed resource. The messages about system calls name the call and give the reason the system gave:

Mapping the keymap the compositor sent (mmap) failed: No such device

connection-error

The connection could not be made, or broke: the compositor went away or the socket failed.

Could not connect to the Wayland display named by $WAYLAND_DISPLAY.
The Wayland connection failed: Broken pipe

connection-error-errno returns the errno value, or NIL if libwayland gave none. After the connection has broken, the display can only be disconnected. When connect fails, check that a compositor is running and that $WAYLAND_DISPLAY and $XDG_RUNTIME_DIR are set in the process's environment. A program started from an IDE or a service often lacks them.

compositor-error

The compositor found that the client broke the protocol, for example by attaching a buffer of the wrong size, and closed the connection. The error is fatal for the connection.

The compositor raised protocol error 2 on wl_surface 4.
Reader Value
compositor-error-interface the interface's protocol name, "wl_surface"
compositor-error-object-id the object's id, 4
compositor-error-code the code, a value of that interface's error enum

To read the code as a keyword, convert it with the interface's error enum:

(wl:enum-keywords 'wl-surface-error (wl:compositor-error-code e))   ; => :INVALID-SIZE

The compositor also sends a human-readable message. libwayland prints it to standard error but does not pass it to the client, so look there. The message names the request at fault.

version-error

A request is newer than its proxy's version, or a bind asks for a version newer than the bindings support. It is signalled before anything is sent, so the connection is unaffected.

WL-SURFACE-OFFSET needs version 5 of wl_surface, but #<WL-SURFACE id 6 v3> has version 3.

version-error-required and version-error-available return the two versions. To fix it, bind the global at a higher version (bind picks the highest available unless :version caps it), or check proxy-version before using newer requests:

(when (>= (wl:proxy-version surface) 5)
  (wl-surface-offset surface dx dy))

For destruction you need no such check: destroy-proxy sends a release request only where the proxy's version has it.

destroyed-proxy-error

A request was sent on a proxy that a destructor request, a destructor event, destroy-proxy or disconnect had destroyed. It is signalled before anything is sent, and cl-wl never touches freed memory.

#<WL-SURFACE destroyed> has been destroyed, so it cannot be used.

destroyed-proxy-error-proxy returns the proxy. Use proxy-live-p to check in advance.

event-error

An event arrived that could not be decoded, such as a string that is not valid UTF-8, or an array of 32-bit integers whose length is not a multiple of 4. Decoding happens inside libwayland's callback, which must not unwind, so the error is held there and signalled by the next dispatch function when control is back in Lisp. The event is dropped. The connection is fine and you may keep dispatching.

An event could not be decoded and was dropped: …

event-error-cause returns the original condition: for an array, a wl-error that names the argument and its length.

missing-global-error

bind found no global of the interface asked for.

The compositor does not offer zxdg_decoration_manager_v1, so ZXDG-DECORATION-MANAGER-V1
cannot be bound.  GLOBAL-VERSION tells whether a global is offered.

For an optional interface, pass :errorp nil and handle NIL.

A broken connection

A connection that has failed stays failed. After a compositor-error or connection-error:

  • requests are dropped silently, since there is nowhere to send them;
  • destructor requests and destroy-proxy still destroy their proxies, so cleanup code works unchanged;
  • flush, dispatch, dispatch-pending, read-events and roundtrip signal the same error again;
  • display-error returns that error without signalling it;
  • disconnect works as usual.

display-error therefore tells a broken connection apart from other failures without provoking one. A typical use is cleanup code that may run after the compositor is gone:

(defun close-window (window)
  ;; Destruction still works on a broken connection; it just sends nothing.
  (dolist (object (list (toplevel window) (xdg-surface window) (surface window)))
    (wl:destroy-proxy object))
  ;; Only flush if there is still someone to talk to.
  (unless (wl:display-error (display window))
    (wl:flush (display window))))

and an error handler that needs to know which kind of failure it is seeing:

(handler-bind ((wl:wl-error
                 (lambda (e)
                   (declare (ignore e))
                   (let ((fatal (wl:display-error display)))
                     (when fatal
                       (log-error "Wayland connection lost: ~A" fatal))))))
  (run-frame))

To recover, disconnect and connect again. Everything bound on the old display is gone and must be created anew.