# How to Debug Hyprland Window Rule Matching: A Complete Guide

> Debug Hyprland window rule matching with trace logging. Monitor CWindowRuleApplicator logs to understand rule application during static and dynamic evaluation. Solve your Hyprland window rule issues now.

- Repository: [Hypr Development/Hyprland](https://github.com/hyprwm/Hyprland)
- Tags: how-to-guide
- Published: 2026-07-26

---

**Enable trace logging with `HYPRLAND_LOG_LEVEL=trace` and monitor the `CWindowRuleApplicator` logs to see exactly why a rule matches or skips during static and dynamic evaluation cycles.**

Hyprland applies window-specific configuration through a sophisticated multi-stage rule system defined in the `hyprwm/Hyprland` source code. When a window rule fails to affect a target application, the issue typically lies in the selector matching logic, the distinction between static and dynamic evaluation, or the rule's enabled state. This guide traces the execution flow from config parsing in `Config::windowRuleMgr()` through the `CWindowRuleApplicator` engine to help you pinpoint why a rule is not taking effect.

## Understanding the Window Rule Architecture

Before debugging, you must understand how Hyprland stores and applies rules. The system separates rule definitions from rule execution, using distinct classes for storage and application.

### Rule Storage and the Window Rule Manager

All window rules are parsed from your configuration file and stored as `CWindowRule` objects inside `Config::windowRuleMgr()`. This manager maintains the master list of rules, but it does not execute them directly. Each rule carries metadata including an `enabled` flag, selector criteria, and effect definitions that determine when and how the rule triggers.

### The Rule Applicator Lifecycle

Every `CWindow` receives a dedicated `CWindowRuleApplicator` instance (accessible via `m_ruleApplicator`) during its construction in [`src/desktop/view/Window.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/desktop/view/Window.cpp) (lines 108‑146). According to the Hyprland source code, this applicator acts as the evaluation engine, responsible for testing window attributes against stored rules and applying the resulting effects. The applicator persists for the window's lifetime, allowing rules to be re-evaluated when properties change.

## Static vs. Dynamic Rule Evaluation

Hyprland evaluates window rules in two distinct phases. Misunderstanding this distinction is a common source of debugging frustration.

### Static Rules and Initial Window Creation

**Static rules** are evaluated once, typically during window creation, and affect immutable properties such as initial workspace assignment, monitor placement, size, or opacity. The evaluation path resides in `CWindowRuleApplicator::applyStaticRule()` in [`src/desktop/rule/windowRule/WindowRuleApplicator.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/desktop/rule/windowRule/WindowRuleApplicator.cpp) (lines 383‑390). If a rule effect is marked as dynamic-only, the static evaluator skips it and emits a TRACE log entry.

### Dynamic Rules and Runtime Events

**Dynamic rules** are evaluated repeatedly on relevant events such as focus changes, title updates, or workspace switches. The method `CWindowRuleApplicator::applyDynamicRule()` (lines 81‑92) handles this continuous evaluation. Effects like `noanim` or `renderUnfocused` require dynamic evaluation because they depend on transient window states rather than initial placement.

## How Rule Matching Works Internally

Rule matching relies on selector engines that compare window attributes against rule criteria.

### Selector Engines and Match Logic

Each rule contains a selector that tests strings (class, title, workspace name, etc.) using specific match engines. The source code implements several engines:

- **Tag engine**: Matches exact tags prefixed with `#`, tested in [`tests/desktop/rule/matchEngine/TagMatchEngine.cpp`](https://github.com/hyprwm/Hyprland/blob/main/tests/desktop/rule/matchEngine/TagMatchEngine.cpp)
- **Regex engine**: Matches patterns prefixed with `~`, tested in [`tests/desktop/rule/matchEngine/RegexMatchEngine.cpp`](https://github.com/hyprwm/Hyprland/blob/main/tests/desktop/rule/matchEngine/RegexMatchEngine.cpp)
- **Integer and Boolean engines**: Handle numeric and true/false comparisons

The selector logic is invoked through `CWindow::matchesStaticSelector()`, which the `CWindowPlacementController::ensurePersistentWorkspacesPresent` method calls at line 54 of [`src/state/WorkspacePlacementController.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/state/WorkspacePlacementController.cpp) to resolve workspace assignments.

### Where Matches Are Triggered

When a window spawns or updates, the applicator iterates through all stored rules in `windowRuleMgr()` and calls the appropriate selector. If the selector returns true, the applicator checks whether the effect is valid for the current evaluation context (static or dynamic) before applying it.

## Enabling Debug Logging for Rule Matching

Hyprland's built-in logger (`Log::logger` defined in [`src/debug/log/Logger.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/debug/log/Logger.cpp)) provides the visibility needed to debug rule evaluation.

### Trace Level Logging

Start Hyprland with the environment variable `HYPRLAND_LOG_LEVEL=trace` (alternatively `HF_LOG_LEVEL=trace` in some builds) to capture detailed TRACE messages from the rule applicator. At this level, the system logs every rule evaluation attempt, including skipped effects and selector mismatches.

### Key Log Messages to Watch

Monitor the log output for specific patterns originating from [`src/desktop/rule/windowRule/WindowRuleApplicator.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/desktop/rule/windowRule/WindowRuleApplicator.cpp):

- **"Skipping effect … not dynamic"**: Emitted at line 92 when `applyDynamicRule()` encounters an effect that cannot be applied dynamically, indicating a static/dynamic mismatch.
- **"BUG THIS: WINDOW_RULE_EFFECT_NONE??"**: An ERR level message at line 114 indicating an unhandled effect type in the rule definition.
- **Selector resolution errors**: `CWorkspacePlacementController::ensurePersistentWorkspacesPresent` logs errors at lines 66‑68 when a rule references a non-existent monitor or workspace that cannot be resolved.

## Step-by-Step Debugging Workflow

Follow this systematic approach to identify why a window rule fails to apply:

1. **Verify config parsing**: Run `hyprctl reload` and check logs for "Parsing window rule" messages. If absent, the rule syntax is invalid or the config file is not loading.

2. **Enable trace logging**: Launch Hyprland with `HYPRLAND_LOG_LEVEL=trace` or set the variable before starting the compositor to capture `CWindowRuleApplicator` activity.

3. **Inspect window state**: For a specific window ID, run `hyprctl getwindowproperty <winid> ruleApplicator` to view active flags such as `renderUnfocused`, `opaque`, or `dimAround`. This confirms which rule properties are currently applied.

4. **Force rule re-evaluation**: Execute `hyprctl reload` or trigger a workspace change (e.g., `hyprctl dispatch exec "[workspace 1]"`) to invoke `CWindowRuleApplicator::recheckStaticRules()` at line 531, re-evaluating rules without restarting the session.

5. **Isolate selector issues**: Create a temporary rule with a unique tag (e.g., `windowrulev2 = float, tag:debugtest`) and watch for TRACE logs indicating whether the selector matches. If no logs appear, the selector engine is rejecting the syntax.

6. **Check monitor resolution**: Ensure rules referencing specific monitors use valid identifiers. The placement controller logs errors when `ensurePersistentWorkspacesPresent` cannot resolve a monitor name.

## Common Pitfalls and Misconfigurations

- **Disabled rules**: Each `CWindowRule` has an `enabled` flag; disabled rules are silently ignored during evaluation.
- **Missing persistent flag**: Workspace rules require `m_isPersistent` to be set; otherwise, `ensurePersistentWorkspacesPresent` skips them during placement resolution.
- **Incorrect selector syntax**: Tags must use `#` prefix, regex requires `~`, and exact matches use no prefix. Syntax errors cause immediate rejection by the match engine.
- **Static/dynamic effect mismatch**: Applying dynamic-only effects (like `noanim`) during static evaluation results in silent skipping with TRACE logs but no error.

## Summary

- **Rule storage**: All rules exist as `CWindowRule` objects in `Config::windowRuleMgr()`.
- **Evaluation engine**: Each window owns a `CWindowRuleApplicator` created in [`src/desktop/view/Window.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/desktop/view/Window.cpp) that handles static and dynamic rule passes.
- **Debugging method**: Use `HYPRLAND_LOG_LEVEL=trace` to view `applyStaticRule()` and `applyDynamicRule()` execution in [`src/desktop/rule/windowRule/WindowRuleApplicator.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/desktop/rule/windowRule/WindowRuleApplicator.cpp).
- **Selector testing**: Verify tag (`#`), regex (`~`), and other selectors against the unit tests in `tests/desktop/rule/matchEngine/`.
- **Common fixes**: Check the `enabled` flag, ensure persistent workspace flags are set, and confirm monitor names resolve in `WorkspacePlacementController`.

## Frequently Asked Questions

### How do I know if my window rule is being parsed correctly?

Run `hyprctl reload` and check the Hyprland logs for entries containing "Parsing window rule". If the rule appears in the logs with its selector and effects listed, the config parser accepted it. Absence of this message indicates a syntax error preventing the rule from reaching `windowRuleMgr()`.

### Why does my rule work for some windows but not others?

This usually indicates a selector mismatch or a static/dynamic phase error. Check whether the target window's class or title matches your selector exactly using `hyprctl clients`. If the selector uses regex, verify the pattern in [`tests/desktop/rule/matchEngine/RegexMatchEngine.cpp`](https://github.com/hyprwm/Hyprland/blob/main/tests/desktop/rule/matchEngine/RegexMatchEngine.cpp). Also confirm the effect is valid for the evaluation phase—some effects only work during dynamic evaluation and are skipped during static application.

### How can I force Hyprland to re-evaluate rules without restarting?

Execute `hyprctl reload` to trigger a full config reload, or change the window's workspace to invoke `CWindowRuleApplicator::recheckStaticRules()` at line 531 of [`WindowRuleApplicator.cpp`](https://github.com/hyprwm/Hyprland/blob/main/WindowRuleApplicator.cpp). This method re-evaluates static rules against the current window state without requiring a compositor restart.

### What does "BUG THIS: WINDOW_RULE_EFFECT_NONE??" mean in the logs?

This ERR level message appears in [`src/desktop/rule/windowRule/WindowRuleApplicator.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/desktop/rule/windowRule/WindowRuleApplicator.cpp) at line 114 when the rule applicator encounters an effect type it does not recognize. This indicates either a corrupted rule object in memory or a rule referencing an effect that exists in the config parser but lacks implementation in the applicator engine. Check your config for typos in effect names and ensure you are running a Hyprland version where the effect is implemented.