How to Troubleshoot Hyprland Startup Issues: A Complete Technical Guide
Troubleshoot Hyprland startup failures by checking XDG_RUNTIME_DIR permissions, validating configs with --verify-config, running in safe mode to isolate plugins, and examining debug logs from src/main.cpp initialization and CCompositor construction.
Hyprland startup issues typically stem from environment misconfigurations, invalid config syntax, or DRM initialization failures. This guide examines the exact launch sequence implemented in the hyprwm/Hyprland source code, tracing how src/main.cpp, ConfigManager, and CCompositor handle initialization to help you diagnose why the compositor exits before displaying windows.
Understanding the Hyprland Launch Sequence
Hyprland follows a strict nine-phase initialization process defined in src/main.cpp and the core compositor classes. Knowing which phase fails allows you to target fixes efficiently.
- Argument Parsing –
main.cppprocesses CLI flags, sets environment variables, and validates the supplied config file path (lines 31-40). - Environment Checks – The compositor verifies
XDG_RUNTIME_DIRexists and prevents accidental root execution unless--i-am-really-stupidis provided (lines 104-108). - Configuration Loading –
ConfigManagerfromsrc/config/ConfigManager.cppresolves the user config path, generates defaults if missing, and instantiates the Lua-based manager. - Compositor Construction –
new CCompositor(verifyConfig)prepares Wayland resources, DRM sync-obj support, and internal managers (src/Compositor.hpp). - Watchdog and Socket Handover – If starting via
start-hyprland, the compositor binds to the provided socket and connects the watchdog pipe when--watchdog-fdor--socket/--wayland-fdarguments are present (lines 71-78). - Safe Mode and Lock Handling – Flags like
--safe-modesuppress user plugins, while--lockedinitiates the screen locker before the main loop (lines 64-66, 78-82). - Server Initialization –
g_pCompositor->initServer()creates the Wayland display, registers global objects, and starts the event loop (implemented insrc/Compositor.cpp). - Runtime Execution –
startCompositor()enters the main loop; any exception thrown during construction triggers immediate termination with error output. - Cleanup – On exit or fatal error,
cleanup()removes lock files and restores file-descriptor limits.
Common Startup Failure Points and Diagnostics
"XDG_RUNTIME_DIR is not set!" Error
This exception originates at line 57 of src/main.cpp when the environment variable is missing or invalid. This commonly occurs on non-systemd init systems.
Verification:
echo $XDG_RUNTIME_DIR
If empty, export a valid path owned by your user:
export XDG_RUNTIME_DIR=/run/user/$(id - u)
Config File Validation Failures
When Hyprland reports "Config file … is invalid", main.cpp (lines 31-40) has failed std::filesystem::canonical or is_regular_file checks. The path may not exist, may be a directory, or lack read permissions.
Diagnostic command:
Hyprland --verify-config && echo "Config OK" || echo "Config errors!"
A non-zero exit indicates syntax errors in ~/.config/hypr/hyprland.conf.
Superuser Restrictions
The compositor refuses to start as root without explicit override. At lines 104-108, main.cpp calls NInit::isSudo() and aborts unless you provide --i-am-really-stupid.
Check your UID:
id -u
If the result is 0, either switch to a regular user or invoke with the override flag (not recommended for daily use).
Socket Handover Incompleteness
When using start-hyprland, the compositor requires both --socket and --wayland-fd simultaneously. The XOR test at lines 131-136 of src/main.cpp emits an error if only one is supplied.
Verify the launch script passes both file descriptors:
Hyprland --socket hyprland --wayland-fd "$FD"
Watchdog Timeouts
The --watchdog-fd parameter (line 71) must indicate a valid file descriptor. If the pipe closes prematurely, the compositor assumes the parent process died.
Inspect watchdog status by running with debug output:
HYPRLAND_DEBUG=1 Hyprland 2>&1 | grep -i watchdog
Compositor Construction Crashes
Exceptions thrown from CCompositor during instantiation (e.g., DRM device initialization failures, missing Wayland backends) are caught at lines 62-67 of src/main.cpp, printing the exception text before abort.
Capture the full stack trace:
HYPRLAND_DEBUG=1 Hyprland
Look for errors originating from CCompositor constructor or initServer().
Plugin Loading Failures
Corrupt or incompatible .so files in your plugin directory cause aborts during PluginManager initialization in src/hyprpm/src/core/PluginManager.cpp.
Check plugin logs:
journalctl -xe | grep -i hyprland
# Or inspect:
cat ~/.local/share/hyprland/log
Diagnostic Commands and Solutions
When Hyprland fails to start, use this systematic approach to isolate the component responsible.
Run in Safe Mode
Safe mode (--safe-mode) disables user-defined plugins and skips risky config sections. If Hyprland starts in this mode, the issue lies in your configuration or a specific plugin.
Hyprland --safe-mode
Validate Configuration Only
Validate syntax without launching the compositor:
Hyprland --verify-config
Exit code 0 confirms valid syntax; any other value indicates ConfigManager detected errors.
Enable Full Debug Logging
Set the HYPRLAND_DEBUG environment variable to capture verbose output from main.cpp initialization and CCompositor setup:
HYPRLAND_DEBUG=1 Hyprland --safe-mode 2>&1 | tee hyprland-debug.log
Check XWayland Initialization
Black screens or missing X11 application support often trace to XWaylandManager failures in src/xwayland/Server.cpp. Enable rendering debug in your config:
debug:disable_logs = false
debug:enable_stdout_logs = true
Then review logs for XWayland server startup messages.
Examine File Descriptor Limits
Low file-descriptor limits cause silent failures during initServer(). The initHelpers.hpp utilities attempt to raise limits, but manual verification helps:
ulimit -n
Values below 1024 may cause resource exhaustion during startup.
Summary
- Environment first: Verify
XDG_RUNTIME_DIRis set and writable before launching Hyprland. - Check permissions: Never run as root without
--i-am-really-stupid; confirm your user owns the runtime directory. - Validate configs: Use
Hyprland --verify-configto catch syntax errors before they crash the compositor. - Isolate problems: Start with
--safe-modeto determine if plugins or config sections cause the failure. - Review logs: Enable
HYPRLAND_DEBUG=1to capture exact failure points insrc/main.cppandCCompositorconstruction. - Socket handover: When using
start-hyprland, ensure both--socketand--wayland-fdarguments are present to avoid initialization aborts.
Frequently Asked Questions
Why does Hyprland say "XDG_RUNTIME_DIR is not set!" on startup?
Hyprland requires the XDG_RUNTIME_DIR environment variable to create Wayland sockets and state files. At line 57 of src/main.cpp, the compositor checks this variable and aborts if missing. Set it to a user-owned directory like /run/user/$(id - u) before launching, typically handled by your login manager or systemd.
What is the difference between --safe-mode and --verify-config?
--safe-mode launches the compositor but disables all user plugins and suppresses certain config-driven features, helping you identify if third-party code causes the crash. --verify-config performs a dry-run validation in ConfigManager and exits immediately with status 0 for valid configs or non-zero for syntax errors, without starting the graphics stack.
How do I fix "Only one of --socket and --wayland-fd supplied" errors?
This error at lines 131-136 of src/main.cpp indicates incomplete socket handover, usually when the start-hyprland wrapper malfunctions. Ensure the script passes both descriptors simultaneously: --socket NAME --wayland-fd NUMBER. Both arguments are required for the compositor to adopt an existing socket.
Why does Hyprland crash immediately without showing the splash screen?
Immediate crashes during CCompositor construction (before setRandomSplash executes) typically indicate DRM initialization failures, missing /dev/dri permissions, or incompatible Mesa/drivers. Run with HYPRLAND_DEBUG=1 to expose the exception thrown during initServer() in src/Compositor.cpp, which normally outputs the specific backend failure to stderr.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →