Troubleshooting¶
When BgRaster doesn't do what you expect, the first place to look is the run's stdout (every event is prefixed # bg-raster: status=...) and the second is lastRun.toml (or lastRun.dry.toml) in the output directory. Both are intended to be greppable and human-readable.
"Nothing happened" — run-skipped-unchanged¶
If you see # bg-raster: status=run-skipped-unchanged and exit code 0 with no PNGs being written, the early-exit fingerprint matched the previous run.
The fingerprint is three things, all of which must match lastRun.toml:
- assembly informational version
- settings hash (computed from your effective config)
- hardware profile (output count, IDs, position, resolution, rotation, DPI)
If you want to force a re-render, do one of:
- delete lastRun.toml from the output directory;
- pass --no-assignment true (the no-assignment path uses lastRun.dry.toml and is treated as a separate fingerprint, but a no-assignment never assigns wallpaper);
- change any setting (even adding a trailing comment to a string value will flip the hash);
- run with --render-output <some-other-path> to bypass the existing lastRun.toml.
The early-exit only triggers when not in no-assignment mode. Dry runs always render.
Wrong PNG resolution / blurry wallpaper¶
The PNG is sized to the output's physical pixel dimensions, queried via EnumDisplaySettingsExW and GetDpiForMonitor. If the resolution looks wrong:
- Check the
[[hardware_output]]block inlastRun.toml—widthPx,heightPx,dpiX,dpiYare recorded as observed. - Confirm the BgRaster.exe manifest is
PerMonitorV2-aware. If you've replaced the manifest or are running an unsigned rebuild, mixed-DPI desktops can report logical (DIP) instead of physical pixels. - Verify you are running in a normal interactive session. Some remote, service, or locked-down sessions can restrict graphics/wallpaper APIs.
Wallpaper didn't apply¶
The most common causes:
- Per-monitor wallpaper unsupported. Older Windows builds (pre-1809) lack the necessary COM contract. Verify Windows version.
- Wallpaper slideshow active. Windows Personalization "slideshow" mode periodically overwrites per-monitor assignments. Disable it.
- Group Policy lockdown. "Prevent changing desktop background" blocks the assignment. Look for
wallpaper-assignment-failedin stdout — the run will exit 1. - Stretched / spanned wallpaper mode. Even if
SetWallpapersucceeds, Windows can render the per-monitor PNG stretched. SetIDesktopWallpaper::SetPositiontoDWPOS_FILLfrom Personalisation, or right-click desktop → Personalize → Background → Picture position → "Fill".
If lastRun.toml shows output-rendered but the wallpaper visibly didn't change, the failure is downstream of BgRaster — check the points above.
"Output-not-found" — configured target doesn't match a real display¶
If lastRun.toml reports output-not-found target=... for a configured [[output]]:
- Integer target (
target = 0) is matched by zero-based index in the order returned byEnumDisplayDevicesW. Reordering displays in Windows Settings can change this index. - String target (
target = "\\\\?\\DISPLAY#...") must be an exact match for theOutputRecord.IdinlastRun.toml's[[hardware_output]]block. Copy-paste it from there into your config to be sure. - The full
Idincludes adapter / monitor instance GUIDs. If you replaced the monitor (even with an identical model) or moved it to a different DisplayPort, theIdwill change.
Run once with no [[output]] entries to capture all hardware outputs in lastRun.toml, then copy IDs into your config.
Duplicate-output-ignored¶
Two [[output]] entries claim the same hardware output. The first wins; the second emits duplicate-output-ignored. Check whether you have:
- two integer targets that resolve to the same index (unlikely);
- an integer target and a string target that both resolve to the same physical output (likely);
- multiple identical string targets.
Remove or re-target the redundant entry.
Slice rendered out of bounds¶
slice-out-of-bounds reason="slice rect (x=...,y=...,w=...,h=...) exceeds output bounds (WxH)" means the resolved slice rectangle (after unit resolution) extends beyond the output. The slice is silently skipped.
Common causes:
- Mixing
vw/vhagainst an output you assumed was 1920×1080 but is actually rotated to portrait (1080×1920). - Rounding from
vmin/vmaxunits producing 1-pixel overhang at certain resolutions. x + width > vwonce both are resolved to pixels.
Adjust dimensions to fit, or use vw/vh instead of px so the slice scales with the output.
Logo doesn't render / orange cross appears instead¶
The orange diagonal cross is the ultimate fallback. If you're seeing it instead of your logo:
- PNG/JPG: confirm the path is correct and the file is readable. Substitution tokens (
${MachineName}, etc.) are applied tologo.source, so a malformed token will produce a missing-file path. - SVG: BgRaster's SVG renderer supports a deliberately small subset —
<rect>,<line>,<path>(M/m/L/l/H/h/V/v/Z only), plusfill,stroke,stroke-width,opacity. Curves (C/Q/A/S/T) are not yet implemented, and unrecognised elements are skipped. Re-author the SVG with straight segments, or convert to PNG. - The first fallback before the orange cross is the embedded
resources/fallback-logo.svg. If you also see the orange cross, the embedded fallback failed to render too — this should never happen and indicates an SkiaSharp issue worth reporting.
Stale files accumulating in the output directory¶
StaleFileCleaner.RecycleFiles is currently a stub: it identifies stale BgRaster PNGs but does not yet move them to the recycle bin (see deferred task 7). They remain on disk and are reported as unrecycledFiles in lastRun.toml. You can safely delete them manually.
The "stale file" heuristic only matches files whose names follow the BgRaster timestamp pattern (yyyy-MM-ddTHH-mm-ss.fffffffZ_<id>.png); other files in the output directory are left alone.
"Round-trip verification failed"¶
If you see LastRunWriter: round-trip verification failed for '...'; previous file kept., the writer produced TOML that did not parse back to the same in-memory state. The previous lastRun.toml is preserved untouched, so this is a diagnostic — the run otherwise completed.
This indicates a serialisation bug. To capture diagnostics:
- Re-run with
--render-output <fresh-dir>to isolate state. - Inspect the leftover
<path>.tmpfile (if it survived) against the in-memory expectation. - File a bug report with both the
.tmpcontent and the previouslastRun.toml.
"Output-discovered" listed for outputs I don't care about¶
output-discovered means BgRaster saw a hardware output that had no matching [[output]] configuration and skipped it. This happens when outputs-skip-unspecified = true (or --outputs-skip-unspecified true).
Default behavior is now to render discovered-but-unspecified outputs using global defaults. If you want the previous behavior (leave unspecified outputs untouched), enable outputs-skip-unspecified.
Performance / startup latency¶
BgRaster is Native AOT and startup should be sub-100 ms even on cold cache. If you're seeing slow runs:
- First run after publish: file system metadata caching can add a few hundred ms. Subsequent runs should be fast.
alternating = true: this fills the bitmap pixel-by-pixel and is O(W×H). For 4K outputs this is a few hundred ms by itself.- Many outputs with high resolution: each output is rendered serially. A 6×4K wall takes proportionally longer.
- PNG encode: at 4K, encoding alone can take 100–300 ms per output depending on disk speed.
If you really need parallelism, the renderer is structured to allow it but currently runs serially for simplicity and predictable temp-file paths.