2026 maintenance note: The complete 2019 body—including its
xcberror, personal fix claim, and image reference—is preserved in a dated archive near the end, with only trailing whitespace normalized. It does not identify the Ubuntu release, Python/PyQt/Qt versions, missing library, or installed package, so it cannot support one universal fix. The maintained guide below diagnoses the active interpreter, Qt installation, display session, and exact loader failure before changing packages.
Table of Contents
Choose one installation route
PyQt5 can come from Ubuntu packages or Riverbank’s PyPI wheels. Both are valid, but the interpreter used to install and run the application must match. Record python --version, the installation command, and the resolved PyQt and Qt versions.
Ubuntu package with system Python
Ubuntu publishes python3-pyqt5 in the Universe component for supported releases:
sudo apt update
sudo apt install python3-pyqt5
python3 -c 'from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR; print("PyQt", PYQT_VERSION_STR, "Qt", QT_VERSION_STR)'
This route uses Ubuntu’s Python and Qt packaging. If APT cannot find the package, check that the correct repositories for the installed Ubuntu release are enabled; do not add a repository copied from a different release.
Project virtual environment with Riverbank wheels
For a project-isolated environment:
sudo apt update
sudo apt install python3-venv
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install PyQt5
python -c 'from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR; print("PyQt", PYQT_VERSION_STR, "Qt", QT_VERSION_STR)'
Riverbank states that PyQt5 wheels install their corresponding Qt libraries. They still rely on operating-system facilities such as the display server and platform-plugin runtime libraries. While the environment is active, use its python; do not install with one interpreter and launch with another. Avoid sudo pip, which crosses the system/package-manager boundary.
Run a minimal, testable application
Save this as hello_pyqt5.py:
import sys
from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR, QTimer
from PyQt5.QtWidgets import QApplication, QLabel
app = QApplication(sys.argv)
label = QLabel(f"PyQt {PYQT_VERSION_STR} / Qt {QT_VERSION_STR}")
label.resize(320, 80)
label.show()
if "--smoke-test" in sys.argv:
QTimer.singleShot(0, app.quit)
raise SystemExit(app.exec_())
Check Python syntax, then launch it from a normal Ubuntu desktop terminal:
python3 -m py_compile hello_pyqt5.py
python3 hello_pyqt5.py
Inside the virtual environment, replace python3 with python. A visible window displaying the PyQt and Qt versions is the expected desktop result. The --smoke-test switch is reserved for automated display tests later in this guide.
What the xcb error does and does not prove
The historical error was:
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized.

The screenshot is historical evidence of the error class, not evidence for a particular missing Ubuntu package.
Qt uses a QPA platform plugin to connect GUI code to the window system. On X11, that plugin is xcb. “Found” only means Qt located a candidate plugin file; loading or initializing can still fail because a shared library is absent, the plugin and Qt libraries are incompatible, the plugin path was overridden, or no usable display is available.
Do not begin by installing a random list of libxcb packages. First determine which of those failure classes applies.
1. Confirm the interpreter and Qt tree
Run diagnostics with the exact interpreter that fails:
command -v python3
python3 --version
python3 - <<'PY'
import PyQt5
from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR, QLibraryInfo
print("PyQt package:", PyQt5.__file__)
print("PyQt version:", PYQT_VERSION_STR)
print("Qt version:", QT_VERSION_STR)
print("Qt plugins:", QLibraryInfo.location(QLibraryInfo.PluginsPath))
PY
In a virtual environment, use python in every line. If PyQt5.__file__ and the plugin directory point into unexpected installations, stop and correct the interpreter or environment instead of copying plugin files between Qt trees.
Qt warns that a global QT_PLUGIN_PATH can interfere with other Qt installations. Inspect overrides:
env | grep -E '^QT_(PLUGIN_PATH|QPA_PLATFORM_PLUGIN_PATH|QPA_PLATFORM)=' || true
As a diagnostic, retry without the two plugin-path overrides:
env -u QT_PLUGIN_PATH -u QT_QPA_PLATFORM_PLUGIN_PATH python3 hello_pyqt5.py
If that works, remove the stale override from the environment configuration rather than replacing it with another hard-coded plugin path.
2. Confirm that a display exists
Inspect the session without modifying access control:
printf 'DISPLAY=%s\nWAYLAND_DISPLAY=%s\nXDG_SESSION_TYPE=%s\n' \
"${DISPLAY-}" "${WAYLAND_DISPLAY-}" "${XDG_SESSION_TYPE-}"
On a local Ubuntu desktop, the session normally provides a usable display connection. In a plain SSH shell, container, service, or CI job, neither display variable may be usable. Installing another XCB library cannot create a display server or its authentication.
Do not use xhost + as a shortcut: it weakens X server access control. Use a properly authenticated desktop/remote-display session or one of the isolated headless test routes below.
3. Ask Qt why the plugin failed
Qt documents QT_DEBUG_PLUGINS for detailed plugin-loading diagnostics:
QT_DEBUG_PLUGINS=1 python3 hello_pyqt5.py 2>qt-plugin-debug.log
Read the first loader error around libqxcb.so, not just the final generic message:
grep -E 'libqxcb|not found|cannot open|undefined symbol|version' qt-plugin-debug.log
The log can contain local paths and environment details. Keep it private and remove or redact it before sharing.
4. Check the installed xcb plugin’s dependencies
Print the platform-plugin directory with the same interpreter:
qt_plugin_dir="$(python3 -c 'from PyQt5.QtCore import QLibraryInfo; print(QLibraryInfo.location(QLibraryInfo.PluginsPath))')"
find "$qt_plugin_dir/platforms" -maxdepth 1 -name 'libqxcb.so' -print
For a trusted plugin installed by Ubuntu or the selected virtual environment, inspect unresolved libraries:
ldd "$qt_plugin_dir/platforms/libqxcb.so" | grep 'not found' || true
If nothing is missing, do not install more XCB packages. Return to the debug log and look for an incompatible symbol/version, unexpected plugin tree, or display connection failure.
If a specific shared object is missing, map that exact filename to the package for the current Ubuntu release:
sudo apt update
sudo apt install apt-file
sudo apt-file update
apt-file search libxcb-xinerama.so.0
For example, install libxcb-xinerama0 only when the diagnostics name libxcb-xinerama.so.0 and the current release’s package index maps it to that package. Package names and dependencies can change by release and architecture. Re-run ldd, the smoke test, and the real application after installing the evidenced package.
5. Separate desktop, offscreen, and virtual-X tests
These tests answer different questions.
An offscreen Qt smoke test constructs the widgets without using xcb:
QT_QPA_PLATFORM=offscreen python3 hello_pyqt5.py --smoke-test
Success proves that Python can import PyQt5 and construct the example with the available offscreen plugin. It does not prove that xcb or the desktop display works.
To exercise the X11/xcb path in headless CI, use an isolated virtual X server:
sudo apt update
sudo apt install xvfb xauth
xvfb-run -a python3 hello_pyqt5.py --smoke-test
xvfb-run creates X authority data, launches Xvfb, runs the command, and cleans up when it exits. A successful Xvfb test plus a failed desktop launch points toward the real desktop session, authorization, or environment. A successful offscreen test plus a failed Xvfb test keeps the focus on xcb and its X11 dependencies.
Do not force QT_QPA_PLATFORM=xcb globally on a Wayland desktop. Platform availability depends on the installed Qt build and session. Leave automatic platform selection alone when it works; use a one-command override only as a documented diagnostic.
6. Read the failure signature before choosing an action
| Evidence | Likely boundary | Next action |
|---|---|---|
ModuleNotFoundError: No module named 'PyQt5' |
Wrong interpreter or PyQt5 not installed there | Select the intended APT or virtual-environment route and verify with that interpreter |
libNAME.so... => not found or “cannot open shared object file” |
Missing runtime library | Use apt-file search for the exact filename, install the mapped current-release package, and rerun diagnostics |
| “undefined symbol”, incompatible Qt version, or unexpected plugin path | Mixed Qt/plugin trees | Remove stale path overrides and rebuild one coherent installation; do not copy libqxcb.so manually |
| “could not connect to display” or empty/unusable display variables | Session/display boundary | Use the logged-in desktop, configured remote display, offscreen smoke test, or Xvfb as appropriate |
| Offscreen passes; Xvfb fails | X11/xcb loading or initialization |
Inspect QT_DEBUG_PLUGINS and ldd results |
| Xvfb passes; desktop fails | Desktop session, authorization, or environment | Compare display variables and Qt overrides between the two sessions |
| All minimal tests pass; the real app fails | Application-specific import, plugin, rendering, or startup code | Reduce the application incrementally and capture the first new error |
7. Avoid fixes that hide the cause
- Do not install every package whose name begins with
libxcb; install the package justified by a missing filename. - Do not copy platform plugins between system Qt, a wheel-provided Qt, Conda, an IDE, or another application.
- Do not set
QT_PLUGIN_PATH,QT_QPA_PLATFORM_PLUGIN_PATH, orLD_LIBRARY_PATHglobally to a path copied from a tutorial. - Do not run the application with
sudoto reach another user’s display or package installation. - Do not use
sudo pipto modify Ubuntu’s Python environment. - Do not treat
QT_QPA_PLATFORM=offscreenas a fix for an interactive desktop application; it deliberately renders without a visible desktop window. - Do not publish plugin debug logs without reviewing local paths and environment data.
A reproducible verification record
Record these results with the issue or project:
- Ubuntu release and architecture from
lsb_release -dsanddpkg --print-architecture; - exact Python executable and version;
- PyQt and Qt versions and
PyQt5.__file__; - installation route and locked project dependencies where applicable;
DISPLAY,WAYLAND_DISPLAY, andXDG_SESSION_TYPEpresence, without sharing session secrets;- Qt plugin directory and the first relevant
QT_DEBUG_PLUGINSloader error; - unresolved
lddentries and the Ubuntu package mapping used; - syntax, offscreen, Xvfb, and real-desktop test results;
- the change made and how it was rolled back.
That record distinguishes a reproducible fix from “installed something XCB-related and it worked.”
—
2019 original export (source archive)
Archive boundary: The text inside the block below is the complete body from
out/posts/2019-04-23-run-pyqt5-in-ubuntu-1898/index.md, exported from post 1898 and dated 23 April 2019. Its wording, punctuation, curly quotes, image path, and personal claim are unchanged; trailing whitespace is normalized. Do not execute it as a current repair procedure.
qt.qpa.plugin: Could not load the Qt platform plugin “xcb” in “” even though it was found. This application failed to start because no Qt platform plugin could be initialized.
I accidentally installed the xcb plugin and the problem solved.

—
Primary and official references
- Riverbank Computing: installing PyQt5
- Riverbank Computing: PyQt downloads and wheels
- Qt 5.15: Qt Platform Abstraction
- Qt 5.15: deploying and diagnosing plugins
- Qt 5.15: X11/XCB requirements
- Qt 5.15: `QLibraryInfo`
- Ubuntu package index: `python3-pyqt5`
- Ubuntu package index: `xvfb`
- Ubuntu manual: `apt-file`
- Ubuntu manual: `xvfb-run`
- Python documentation: virtual environments
- Python documentation: `py_compile`
