diff --git a/src/sxwm.c b/src/sxwm.c
--- a/src/sxwm.c
+++ b/src/sxwm.c
@@ -3235,8 +3235,17 @@
 	if (!XQueryTree(dpy, root, &root_ret, &parent_ret, &children, &n_children))
 		return;
 
-	int screen_w = scr_width;
-	int screen_h = scr_height;
+	/* scr_width/scr_height are only refreshed by update_mons(), on
+	 * startup and root ConfigureNotify -- unlike reserve_top/reserve_left
+	 * (measured from a monitor's own x/y, independent of total screen
+	 * size), reserve_bottom/reserve_right are measured as a distance from
+	 * the *opposite* edge of the whole screen, so a stale cached
+	 * scr_width/scr_height silently corrupts only those two, never
+	 * left/top. Query the server directly here instead of trusting the
+	 * cache, since this runs cheaply and infrequently (on retile, not
+	 * per-frame). */
+	int screen_w = XDisplayWidth(dpy, DefaultScreen(dpy));
+	int screen_h = XDisplayHeight(dpy, DefaultScreen(dpy));
 
 	for (unsigned int i = 0; i < n_children; i++) {
 		Window w = children[i];
