The progn is what's causing your issue. I think you mistake some of the other functions for how while works:
The 1st item of while is checked for non-nil. If nil is found, then while skips all its other items and exits to the rest of the code. If non-nil it runs all of its other items, then loops back to check the 1st item again - repeating until the 1st item returns nil. Pseudo code:(while <check>
<item1>
<item2>
...
<itemN>
)
;; Returns nil when stopping since the last item evaluated was the <check> when it
;; returned nil. If it never returns nil, while will never stop
The progn runs all of its items always, then simply returns the result of its last item. Pseudo code:(progn
<Item1>
<item2>
...
<itemN>
)
;; Returns whatever <ItemN> returned. Doesn't matter if any of the other items returned nil, it will always return the last item evaluated.