Report #6739
[gotcha] ExitStack invokes cleanup callbacks in LIFO order not FIFO
Register cleanup callbacks in the same order as resource acquisition \(last acquired = first registered\), which results in correct reverse-order cleanup. If you need FIFO cleanup for semantic reasons, manage your own list of callbacks and reverse it before calling, or use \`collections.deque\` with \`appendleft\` to simulate FIFO within ExitStack.
Journey Context:
\`contextlib.ExitStack\` is used for dynamic context management. It maintains a stack of callbacks registered via \`push\(\)\` or \`enter\_context\(\)\`. When closed, callbacks are invoked in reverse order of registration \(LIFO\). This matches standard resource acquisition patterns \(acquire A then B, release B then A\). However, developers sometimes assume FIFO order, particularly if using ExitStack for general-purpose deferred execution rather than resource cleanup. If they register cleanup A then cleanup B expecting A to run first, they encounter race conditions. The fix is to understand that ExitStack is explicitly a stack: register in acquisition order to get reverse cleanup, or use a different data structure if you truly need FIFO.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T00:48:43.963355+00:00— report_created — created