Skip to main content

hyprvalidate

Migrate old Hyprland configs to Lua.
Validate Lua configs against Hyprland's own API schema.

Try it online · Install · Why it's different

CI License: MIT Python 3.10+ Hyprland 0.55+ tests


Hyprland 0.55 replaced the old hyprland.conf (hyprlang) format with a Lua config. If you have a config from before that, it needs migrating — and if you already wrote a Lua one, nothing currently tells you whether it's actually valid until Hyprland refuses to load it.

hyprvalidate does both, by reading the API description Hyprland already ships (/usr/share/hypr/stubs/hl.meta.lua — autogenerated from the compositor's own source on every build) rather than a table someone typed out by hand.

$ hyprvalidate convert ~/.config/hypr/hyprland.conf --split ~/.config/hypr
wrote 10 file(s) to /home/you/.config/hypr/ (entry point: hyprland.lua)
  appearance.lua
  autostart.lua
  devices.lua
  env.lua
  hyprland.lua
  input.lua
  keybinds.lua
  monitors.lua
  plugins.lua
  windowrules.lua

$ hyprvalidate check ~/.config/hypr
10 file(s) checked, no issues found.

Install

pipx (recommended — no clone, isolated environment):

pipx install git+https://github.com/Paritsingla7/hyprvalidate.git
Other methods, and Arch/PEP-668 notes

If pipx isn't installed, on Arch use sudo pacman -S python-pipx — pip install pipx is blocked by default because the system Python is externally managed (PEP 668).

From a clone:

git clone https://github.com/Paritsingla7/hyprvalidate.git
cd hyprvalidate
python3 -m venv .venv && .venv/bin/pip install .

Prefer a venv over --break-system-packages on Arch and other externally-managed Pythons.

Arch (PKGBUILD): a ready packaging/PKGBUILD is in the repo.

Requires: Python 3.10+, and luac (pacman -S lua) for the syntax gate. Hyprland 0.55+ is needed to read the live schema — or pass --stub schema.json to run without Hyprland installed at all.

Usage

Convert an old config

# straight to stdout
hyprvalidate convert hyprland.conf

# one file
hyprvalidate convert hyprland.conf -o hyprland.lua

# a modular directory (recommended)
hyprvalidate convert hyprland.conf --split ~/.config/hypr

--split writes one file per config area with a hyprland.lua entry point that require()s the rest — the layout Hyprland's own docs recommend ("you can (and should!!) split this configuration into multiple files"), rather than a single 400-line dump.

Symbol names and value types are looked up in the schema. Anything the converter can't resolve confidently becomes a comment, never a silent guess:

-- TODO(hyprvalidate convert): unrecognized old dispatcher 'frobnicate' -
-- no confident rename, convert manually (hyprland.conf line 84)

The converter runs the full validator on its own output before reporting success. If a conversion doesn't come out clean, you find out immediately rather than when Hyprland reloads.

Validate a Lua config

hyprvalidate check ~/.config/hypr/hyprland.lua
hyprvalidate check ~/.config/hypr          # a whole directory

Catches unknown dispatchers, invalid config keys, wrong value types, bad call arity, and invalid fields inside a spec table:

$ hyprvalidate check hyprland.lua
hyprland.lua:14: [unknown_spec_field] 'resolution' is not a field of HL.MonitorSpec
hyprland.lua:47: [type_mismatch] 'animations.enabled' expects boolean, got string ('yes')

2 issue(s) found.

Exit codes: 0 clean · 1 schema findings · 2 invalid Lua, unparseable input, or a missing schema. Scriptable in CI.

Validating dotfiles in CI (no Hyprland needed)

--stub accepts a schema.json snapshot as well as the live stub, so you can check a dotfiles repo on a runner that has no compositor installed:

- run: pipx install git+https://github.com/Paritsingla7/hyprvalidate.git
- run: hyprvalidate check hypr/ --stub schema.json

Why this exists

Four community converters already existed when this started. Testing all four against the same real config found that three produce output that isn't valid Lua at all, and that all four hand-transcribe Hyprland's dispatcher and config-key names into their own tables — tables which have drifted from the real API in different directions (measured: 53, 11, and 2 dispatcher names, against a real count of 51).

Meanwhile Hyprland ships the correct answer, regenerated on every build. One of those four tools — hypr2lua — already had the right idea and tried to read it. Its loader hits a one-line bug (strings.HasPrefix(line, "--") discards all 1736 annotation lines in the file, leaving 0 entries extracted), so the schema it threads through its pipeline is always empty. The idea was right; the read failed silently.

That's the whole thesis here: extract the real schema properly, look everything up against it, and never hardcode a name you could have asked for.

Full measured comparison, with a script to reproduce every number, and proper credit to the prior work: docs/COMPARISON.md.

Limitations

Stated up front, because a tool whose whole point is honesty about uncertainty shouldn't hide its own gaps.

  • plugin { } blocks aren't converted. Plugin config is registered at runtime by each plugin and is genuinely absent from the schema, so there's nothing to validate against. Flagged as a TODO for manual conversion.
  • source = isn't inlined. Sourced files are reported, not followed — convert them separately.
  • animation / bezier list directives aren't mapped yet (they're not in the schema's flat config-key space).
  • Window-rule fields aren't deeply checked. HL.WindowRuleSpec only types 3 universal fields in the stub; per-rule-type fields are dispatched dynamically and aren't enumerable, so checking them would produce false positives. Deliberately skipped rather than guessed.
  • $variables are inlined, not preserved as a shared Lua table — so you lose the single-point-of-change that $terminal gave you.
  • Untyped functions aren't arity-checked. Many stub functions are typed fun(...) with no named parameters; there's nothing to check against, so they aren't.
  • No --fix mode yet. The validator reports; it doesn't repair.

How it works

hyprland.conf ──lexer──▶ hyprlang AST ──mapper──▶ Lua AST ──▶ .lua file(s)
                                            ▲                      │
                    hl.meta.lua ──extractor─┤                      │
                        (the schema)        │                      ▼
                                            └──────── checker ◀── luac -p

Everything routes through one extracted schema. The mapper asks it what a symbol is called and what type a value should be; the checker asks it whether what's on disk is real. Neither has a hardcoded API table.

Component What it does
schema/extractor.py hl.meta.lua → queryable schema (79 classes, 353 config keys, 51 dispatchers)
hyprlang/ lexer + parser for the old .conf format
luaast/ Lua reader, writer, and the luac -p gate
converter/ block dispatch, type coercion, rename tables, TODO emission, mapper
checker.py the validator: symbols, config keys, types, arity, spec fields
cli.py convert / check

Contributing

Bug reports about wrong conversions are especially useful — attach the hyprlang snippet and what you expected. See CONTRIBUTING.md.

git clone https://github.com/Paritsingla7/hyprvalidate.git && cd hyprvalidate
python3 -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest tests/ -q

The suite runs with or without Hyprland installed (it falls back to the committed schema.json).

Acknowledgements

  • Hyprland — ships the machine-readable API stub this whole project reads; no schema, no project.
  • hypr2lua — reached the schema-driven idea first (see "Why this exists" above).
  • hyprconf2lua — its MIT-licensed hyprlang lexer is adapted here (attributed in hyprvalidate/hyprlang/lexer.py) rather than rewritten from scratch.
  • hyprlang2lua and hypr-migrate — output worth studying; reading all four is what made the problem visible.
  • luaparser (MIT) — the Lua grammar.

License

MIT.

Metadata

Release files for hyprvalidate 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for hyprvalidate 0.2.0
File Size Uploaded
hyprvalidate-0.2.0.tar.gz 57.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for hyprvalidate 0.2.0
File Interpreter ABI Platform
hyprvalidate-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 98.3 kB

Release files / hyprvalidate-0.2.0.tar.gz

Download URL hyprvalidate-0.2.0.tar.gz
Size 57.5 kB
Tags Source
SHA-256 checksum
How to use checksums
93d20c72d33d92120a78a9aa345f278373d305534f96b30407664a9f728957e3
BLAKE2b-256 checksum
How to use checksums
445d9665a8493a09a35ae3719849d754d143dcc92017dded3c021e5b4720c004
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 17, 2026.

Transparency log

Release files / hyprvalidate-0.2.0-py3-none-any.whl

Download URL hyprvalidate-0.2.0-py3-none-any.whl
Size 40.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cae3ccf6c53b4aafd60fc2a6c7ab835a2976a7954dfe1086dcebbbd1c719e9ea
BLAKE2b-256 checksum
How to use checksums
66719504b92f4400882aa93ead923f7b000159df91db01ea91521208596bb728
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 17, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.0

2 release files

0.3.0

2 release files

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page