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.

  1. Argument Parsing – main.cpp processes CLI flags, sets environment variables, and validates the supplied config file path (lines 31-40).
  2. Environment Checks – The compositor verifies XDG_RUNTIME_DIR exists and prevents accidental root execution unless --i-am-really-stupid is provided (lines 104-108).
  3. Configuration Loading – ConfigManager from src/config/ConfigManager.cpp resolves the user config path, generates defaults if missing, and instantiates the Lua-based manager.
  4. Compositor Construction – new CCompositor(verifyConfig) prepares Wayland resources, DRM sync-obj support, and internal managers (src/Compositor.hpp).
  5. Watchdog and Socket Handover – If starting via start-hyprland, the compositor binds to the provided socket and connects the watchdog pipe when --watchdog-fd or --socket/--wayland-fd arguments are present (lines 71-78).
  6. Safe Mode and Lock Handling – Flags like --safe-mode suppress user plugins, while --locked initiates the screen locker before the main loop (lines 64-66, 78-82).
  7. Server Initialization – g_pCompositor->initServer() creates the Wayland display, registers global objects, and starts the event loop (implemented in src/Compositor.cpp).
  8. Runtime Execution – startCompositor() enters the main loop; any exception thrown during construction triggers immediate termination with error output.
  9. 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_DIR is 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-config to catch syntax errors before they crash the compositor.
  • Isolate problems: Start with --safe-mode to determine if plugins or config sections cause the failure.
  • Review logs: Enable HYPRLAND_DEBUG=1 to capture exact failure points in src/main.cpp and CCompositor construction.
  • Socket handover: When using start-hyprland, ensure both --socket and --wayland-fd arguments 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →