Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

FAQ

Symptoms whose cause lives on another page: find the symptom, then follow the link. A trap that stays within one page is in that page’s Gotchas table.

First steps when something is wrong

StepCommandTells you
1mantle checkSyntax and top-level errors, with file and line. Node and layout errors as laid out with every capability nil, then with sample data (what check covers)
2mantle logEvaluation errors, layout errors, errors raised in callbacks, failed mantle calls and refused mantle set/toggle writes (output and logging)
3MANTLE_DUMP_LAYOUT=<id>@<output> mantle -vvvEvery visible node’s kind and rect on that surface after each pass (how do I)

Nothing shows

SymptomCauseFix
No surface appears at all after startingThe startup evaluation raised, so there is no scene. The error is in the logEvaluation, reload and generations
mantle check passes, but a surface is empty or lays out wrongcheck lays out on a 1920x1080 output with every capability nil, then with one sample value each, so a branch that needs a particular value, or a smaller output, went unchecked. The running shell reports those in mantle logWhat check covers, then the layout dump and the layout model
A node shows before its data arrivesThe map returns nil for visible, which counts as absent, and visible defaults to truesignals gotchas
Two bars on screenTwo shells are running, one per mantle startcli gotchas
The shell vanishes and comes back only after 30 sThe Renderer died three times within 60 s, so the next respawn waitsFix the error in mantle log (limits)

A save or a click does nothing

SymptomCauseFix
Saving a file leaves the old UI on screen, and mantle.rescue.is_rescue is trueThe reload failed: an evaluation or apply error keeps the previous scene, sets mantle.rescue and logs the error. The next reload that applies clears itFind out why a reload did nothing, error banner
After a broken save, mantle call says the action does not exist, timers stop and on_change goes quietA failed reload drops every action, timer, handler and idle threshold the last evaluation registeredEvaluation, reload and generations
An on_change, timer, process.run, palette or idle callback does nothingIts error, a blown CPU budget included, is a warning. Read mantle logOutput and logging
mantle.<cap>:<action>(...) returns nil and nothing changesActions are fire and forget; a wrong argument type or count is dropped with a log lineactions
A keybind running mantle set or mantle toggle does nothingIt was refused (an undeclared name, a bare toggle on a non-boolean). The compositor discards the error; mantle log keeps itcli gotchas
Saving a .json or an image beside shell.lua does not reloadOnly .lua and .frag changes reload; a byte-identical save and an unreadable directory (changes inside it will not reload) do not eitherEvaluation, reload and generations
An edit to fonts { ... } does nothingThe font chain is read when the Renderer startsRestart the shell (fonts)
A textfield shows no caret and takes no keysThe panel does not take keyboard focustext fields, panel

Values are wrong or stale

SymptomCauseFix
A map raises attempt to index a nil value at startup, or a capability reads nilEvery capability reads nil until its first push, for mantle check’s first pass, and for good when its backend is missingThe one rule, capabilities
A text never updatesIt holds a :get() snapshot, not the signalThe one rule
A setting is lost after the shell restartsNamed state lives in the Renderer and dies with itpersistent_table
A switched view keeps old state, or snaps in without its animationvisible = false freezes the subtree in place; two id-less views of the same kind are reusedSwitching views, nodes

Errors in the log

MessageCauseFix
surface 2 is a string, not a noderequire returned the module and its path into the surface listModules and require
exceeded the 5ms CPU budget for one evaluationA map, computed, handler or timer did too much workLimits and budgets
signal nesting exceeded its maximum depth of 32 levelsA derived chain reads itself or nests too deepErrors
a Signal resolved to another SignalA map returned a signalErrors
`margin.left` is a Signal handleA signal nested in a property table does not resolvesignals gotchas
`mantle` asked to write state ... and was refusedmantle set/toggle named an undeclared state, or wrote a value it refusesValues and arguments

Running processes

SymptomCauseFix
process.run prints nothing and exit_cb gets nilThe spawn failed, usually a command not on PATH. mantle log has the reasonprocess.run
A program runs twice after a saveA top-level process.detach launches again on every reloadsession_process, What survives a reload

Capabilities

A capability starts on the config’s first mantle.<name> read, so a config that never reads it runs no server, watcher or agent. The quoted log lines need -v or -vv.

SymptomCauseFix
A capability reads nil or stays inertIts backend is missing or started after the shell (requirements); sysinfo and updates wait for configure; or the config reads mantle.<name> only inside a callback that never ranStart the backend, then restart the shell: a reload does not retry. Call configure where the page says so. Read mantle.<name> at config top level
battery.present is falseUPower’s display device is not a present battery (desktop, or battery not detected), or UPower is not running (state "Unknown")Expected on desktops; otherwise start upower.service, then restart the shell; upower -d should list DisplayDevice
No tray iconsConfig never reads mantle.tray; another host (waybar, snixembed) owns org.kde.StatusNotifierWatcher; the app is XEmbed-only; or it registered at an unlisted path before the shell startedRead mantle.tray; stop the other host; restart the app so it registers again
Notifications not showingAnother daemon (mako, dunst, swaync) owns org.freedesktop.Notifications (another notification daemon already owns this name), or the config never reads mantle.notificationsStop and disable the other daemon, then restart the shell. busctl --user status org.freedesktop.Notifications names the owner
No notification soundDND or quiet on (critical still plays); the app is muted; the file is not Ogg Vorbis or 16-bit WAV, is over 4 MiB or 30 s, or lies outside the sound roots; sound-name with no tier sound registerednotifications backend
Polkit prompts not appearingConfig never reads mantle.polkit, so the agent never registers; another agent (polkit-gnome, hyprpolkitagent) registered first; $XDG_SESSION_ID unsetRead mantle.polkit; stop the other agent and restart the shell; start the session through logind
Polkit authentication always fails/run/polkit/agent-helper.socket is missing, so the helper cannot runCheck that the installed polkit provides that socket
Idle never firesA block-mode idle inhibitor is held (systemd-inhibit --list), a browser or player holds ScreenSaver, a Wayland surface inhibitor is up, or the config’s own inhibit is still held; or the compositor lacks ext_idle_notifier_v1mantle.idle.inhibited and inhibitors name the holder (empty who is the compositor)
Unlock refuses the right passwordPAM stack login in use and pam_nologin or pam_shells refusingInstall the mantle PAM stack (install)
Locked session with no lock screenThe Renderer died and its replacement could not retake the lock (could not take the session lock over)Switch VT and unlock through the compositor’s own mechanism
Keyboard layout switch does nothingNot niri or Hyprland; only one layout configured (layout_count 1); on Hyprland it sends switchxkblayout main <i> to the keyboard marked main, which likely fails on Hyprland 0.56+, whose socket parses LuaConfigure several layouts in the compositor; on Hyprland 0.56+, switch through a compositor keybind
Caps/Num Lock always falseNo readable /dev/input keyboard with LEDs and no sysfs LEDGive the user read access to the input device
brightness reads nilNo /sys/class/backlight device; external monitors are not coveredNone; brightness is backlight-only
Brightness writes ignoredSession not active (another VT), so logind refuses SetBrightnessSwitch back to the session
Pairing prompt never showsThe adapter is not visible and this shell did not start the pairing, or the device asks for a PIN or passkey entry (rejected)Make the adapter visible or pair from the shell

See also: runtime, cli, signals, processes, capabilities, glossary (rescue, hydration, generation).

Source: reload and rescue, apply, check, callback logging, log subsystems.