I have shipped plenty of Python systems where the loop is the heartbeat: CLIs that keep asking for input, services that wait for jobs, bots that retry flaky APIs. In those moments, the simplest and most dependable pattern is the infinite loop with an internal exit condition. That is what while True gives you—an always-on loop that you explicitly stop when your program decides it is time. If you use it deliberately, it is one of the clearest ways to model ‘keep going until something inside the loop says stop.‘
In this post, I show you how to use while True safely and effectively. You will see concrete, runnable examples, common mistakes I have seen in code reviews, and the patterns I rely on when building production-grade scripts or services. I also show when not to use while True, because there are scenarios where a regular while condition or a for loop is the better choice. My goal is to help you think about infinite loops as a tool, not a trap.
The Core Idea: An Always-True Loop With an Internal Exit
A while loop in Python runs as long as its condition evaluates to True. When you write while True, the condition is literally always true, so the loop runs forever—unless you break out of it.
The pattern is simple:
while True:
# do work
if some_condition:
break
The magic here is that the loop’s exit condition lives inside the loop body. That is a major difference from the classic while condition form. With while True, you are free to check different exit conditions at different points, early-return, or exit from multiple branches. This becomes especially useful when you need to:
- Keep prompting for input until it is valid
- Listen for events or connections indefinitely
- Retry an operation until it succeeds
Because the loop never stops on its own, you must define a clear path to exit. That is a non-negotiable discipline for stable code.
A First Look: The Smallest Useful while True
Here is a minimal loop that exits when a counter reaches zero. It shows the basic control flow without extra noise:
count = 5
while True:
print(f‘Count is {count}‘)
count -= 1
if count == 0:
break
print(‘Done‘)
Key observations:
- The loop does not care about the counter in its condition.
- The exit logic is explicit and local; you can read it top to bottom.
- The loop can exit at any point, not just at the bottom.
This is exactly why I reach for while True when the exit condition is naturally inside the work, not outside it.
Example 1: Input Validation That Never Lies
A classic use case is user input. If you need to collect valid data, you want the prompt to continue until the data is correct. Here is a real-world approach using a numeric range:
while True:
raw = input(‘Enter your age (18-120): ‘)
try:
age = int(raw)
except ValueError:
print(‘Please enter a whole number.‘)
continue
if 18 <= age <= 120:
print(f‘Accepted: {age}‘)
break
print(‘Age must be between 18 and 120.‘)
Why I like this pattern:
- I can keep parsing and validating inside the loop.
continuehandles recoverable errors without extra nesting.- I avoid booleans like
is_valid = Falsethat can make the logic harder to follow.
If you have ever seen input validation written as a while not valid: loop with many nested conditions, this pattern is usually clearer.
Example 2: Controlled Infinite Loop for Background Work
Let us say you are building a simple job runner that checks a queue for tasks. You want it to wait, work, and stop only when an exit signal is received. Here is a simplified loop using a mocked signal and a short delay to avoid CPU burn:
import time
shutdown_requested = False
while True:
if shutdown_requested:
print(‘Shutdown requested. Exiting.‘)
break
job = None # Replace with real queue polling
if job is None:
print(‘No jobs. Sleeping...‘)
time.sleep(1.0)
continue
print(f‘Processing job: {job}‘)
# process_job(job)
What matters here:
- The loop can run forever without chewing CPU thanks to
sleep. - The exit condition is checked at the top for fast shutdown.
- You can easily add signal handlers or file-based flags.
This is the backbone of many simple services. In 2026, I still use this exact pattern for small workers that do not need a full async stack.
Example 3: Retry Logic With Safe Limits
Retry loops are a perfect match for while True, especially when the success condition comes from inside the loop. Still, you should usually protect your system with a max attempt count or backoff strategy.
import time
attempt = 0
max_attempts = 5
while True:
attempt += 1
print(f‘Attempt {attempt}...‘)
success = False # Replace with real operation
if success:
print(‘Operation succeeded‘)
break
if attempt >= max_attempts:
print(‘Giving up after max attempts‘)
break
time.sleep(0.5)
I am explicit here because infinite retry loops are dangerous in production. Without limits, a failing dependency can silently burn resources forever.
When while True Is the Best Tool
I reach for while True when the loop’s exit logic belongs inside the loop. Here are strong cases where it shines:
1) You need early exits from multiple branches
while True:
command = input(‘> ‘).strip().lower()
if command == ‘quit‘:
break
if command == ‘help‘:
print(‘Commands: help, quit, status‘)
continue
if command == ‘status‘:
print(‘All systems nominal‘)
continue
print(‘Unknown command‘)
2) You are waiting for events with a loop that never ends, but should remain responsive to shutdown signals or errors.
3) You want validation or parsing to happen with continue and break rather than stacked flags.
4) The number of iterations is unknown or open-ended.
In these cases, a while True loop is not only fine—it is often the most readable option.
When You Should Avoid while True
I discourage while True when the loop count is known or when a simple condition can sit in the while header. These cases often become more readable with standard loop constructs.
Avoid it when:
- You already know the end condition before the loop starts.
- The loop can be expressed cleanly as
while condition. - You are iterating a finite sequence—use
forinstead.
For example, this is better as a classic while:
n = 10
sum_total = 0
while n > 0:
sum_total += n
n -= 1
print(sum_total)
And this is better as a for loop:
sum_total = 0
for n in range(10, 0, -1):
sum_total += n
print(sum_total)
In other words, if while True only forces you to add break at the very end, you probably did not need it.
Common Mistakes I See in Code Reviews
Here are the recurring issues I watch for when reviewing while True loops, along with fixes.
1) Missing an Exit Path
A loop with no break is a bug unless you truly intend it to run forever (like a server process). Example of a problem:
while True:
data = input(‘Enter value: ‘)
print(data)
This is fine for a REPL-like tool, but if the goal is ask once and proceed, the loop never ends. The fix is obvious: add a condition and break.
2) Busy Waiting That Burns CPU
If your loop checks a condition and does nothing else, it can burn 100% CPU. Example:
while True:
if is_ready():
break
Add a short sleep to reduce CPU usage:
import time
while True:
if is_ready():
break
time.sleep(0.1)
In practice, I tune the sleep to the responsiveness you need. For human-facing tools, 50–200ms feels responsive. For backend polling, 500ms to a few seconds can be fine.
3) Swallowing Exceptions Silently
Infinite loops that swallow errors can hide failures. A minimal safe pattern:
import logging
while True:
try:
# do work
pass
except Exception as exc:
logging.exception(‘Loop error: %s‘, exc)
break
If you want the loop to keep running after certain errors, catch only those specific exceptions and keep a limit on retries.
4) Overly Nested Logic
If you find three or four levels of indentation inside a while True, it is time to refactor. Use continue to flatten conditions:
while True:
command = input(‘> ‘)
if not command:
print(‘Empty input‘)
continue
if command == ‘quit‘:
break
# process command
This reads better and reduces error-prone nesting.
Break, Continue, and Return: Control Flow Done Right
while True is most powerful when combined with explicit control flow. Here is how I think about each keyword:
break: Exit the loop entirely.continue: Skip the rest of the loop and start the next iteration.return: Exit the current function completely.
Here is a function that uses all three in a clear way:
def getvalidport():
while True:
raw = input(‘Enter a port number (1-65535): ‘)
if raw == ‘quit‘:
return None
try:
port = int(raw)
except ValueError:
print(‘Please enter a number.‘)
continue
if 1 <= port <= 65535:
return port
print(‘Port out of range.‘)
Notice how I avoid extra state variables. The loop exits naturally based on the function’s needs. This is the cleanest pattern for interactive helpers.
Performance and Resource Considerations
while True loops can run for a long time, which means performance and resource hygiene matter. Here are the issues I watch closely:
CPU Usage
If your loop polls a condition, add a delay. Without it, Python will run the loop as fast as possible, typically consuming a full CPU core. In many systems, a short sleep between 50ms and 500ms is enough to keep things responsive without wasting resources.
I/O Blocking
If the loop blocks on I/O (like input() or socket reads), that is usually fine. The loop will not consume CPU while waiting. But if you are doing network I/O, handle timeouts so the loop does not hang forever when you want it to exit.
Memory Growth
Be careful not to accumulate data inside a long-running loop. Appending to a list forever will eventually exhaust memory. If you need to retain history, cap it or store it externally.
Logging Volume
In a fast loop, logging every iteration can flood logs and slow your system. I usually log only on state changes or errors.
These are basic operational concerns, but they become critical when the loop runs 24/7.
Using while True With Timeouts
Sometimes you want the loop to keep trying for a fixed period of time. Here is a pattern using a deadline:
import time
timeout_seconds = 5.0
start = time.monotonic()
while True:
if time.monotonic() - start > timeout_seconds:
print(‘Timed out‘)
break
ready = False # Replace with real check
if ready:
print(‘Ready!‘)
break
time.sleep(0.1)
This is a more flexible alternative to a fixed number of retries, and I use it when the operation duration is unpredictable.
Using while True With External Signals
Long-running loops often need to respond to external shutdown signals. A basic approach uses a shared flag. Here is a pattern that works well in scripts or small services:
import signal
shutdown = False
def handle_signal(signum, frame):
global shutdown
shutdown = True
signal.signal(signal.SIGINT, handle_signal)
while True:
if shutdown:
print(‘Shutdown signal received‘)
break
# do work
This keeps the loop responsive to Ctrl+C and allows you to close resources cleanly.
Traditional vs Modern Usage Patterns
Here is a quick comparison of how this pattern has evolved in modern codebases, especially with more AI-assisted workflows and automation tools in 2026.
Traditional Pattern
—
while True + break
Infinite loop with break
Raw infinite loop
Inline while True
run() function for testability The modern improvement is not about changing while True itself—it is about wrapping the loop with better safety and observability.
Real-World Scenario: CLI Menu With Safe Exit
Here is a complete example of a simple menu-driven CLI that uses while True in a clean and testable way:
def show_menu():
print(‘\nMenu‘)
print(‘1) Create report‘)
print(‘2) View status‘)
print(‘3) Exit‘)
def run_cli():
while True:
show_menu()
choice = input(‘Select an option: ‘).strip()
if choice == ‘1‘:
print(‘Generating report...‘)
# create_report()
continue
if choice == ‘2‘:
print(‘Status: OK‘)
continue
if choice == ‘3‘:
print(‘Goodbye‘)
break
print(‘Invalid option. Try again.‘)
if name == ‘main‘:
run_cli()
This is a perfect use of while True because you need multiple early exits and continuous prompting. The loop reads like a state machine with simple states.
Subtle Edge Cases and How I Handle Them
1) break in a try Block
If you break inside try, the finally block still runs. That is good, but it can surprise people. Be intentional:
while True:
try:
data = input(‘Data: ‘)
if data == ‘quit‘:
break
finally:
# This always runs
pass
I rarely use finally inside loops unless I am managing resources.
2) Nested Loops
Breaking from an inner loop does not exit the outer loop. If you need to exit multiple levels, use a flag or refactor into a function and return.
def run():
while True:
while True:
cmd = input(‘> ‘)
if cmd == ‘quit‘:
return
This is often clearer than a break flag.
3) Accidental Infinite Loop in Testing
If you use while True in a function, make sure it has a way to terminate during tests. Dependency injection helps. Example:
def poll(get_job):
while True:
job = get_job()
if job is None:
break
In tests, you can feed a sequence that ends with None, ensuring the loop stops.
Better Readability With Guard Clauses
I prefer guard clauses—early exits—to reduce nesting. This makes while True loops clean and linear.
while True:
token = input(‘Token: ‘).strip()
if not token:
print(‘Empty token‘)
continue
if token == ‘quit‘:
break
if len(token) < 10:
print(‘Token too short‘)
continue
# main work happens here
print(‘Token accepted‘)
This style keeps the happy path visible and makes error handling obvious.
The while True + else Pattern (Yes, It Exists)
Python allows an else clause on loops. The else runs only if the loop ends normally (no break). With while True, that seems weird at first, but it is useful when you implement a retry loop where you expect to break on success, and else marks failure after a limit.
max_attempts = 3
attempts = 0
while True:
attempts += 1
ok = False # Replace with real call
if ok:
print(‘Success‘)
break
if attempts >= max_attempts:
# Normal loop end
break
else:
# This will not run in this exact structure
pass
In practice, while True with else is less common than with a bounded while condition. When I need else, I usually prefer a for loop over a range, because it is clearer:
for attempt in range(3):
ok = False
if ok:
print(‘Success‘)
break
else:
print(‘Failed after 3 attempts‘)
That said, do not be surprised if you see else on loops in production code; it can be a tidy way to express a failed search.
Alternatives That Sometimes Read Better
while True is not the only way to express open-ended looping. Here are alternatives I often consider:
1) iter(callable, sentinel) for Polling
If you are repeatedly calling a function until it returns a sentinel value, Python has a built-in pattern:
def read_line():
return input(‘> ‘)
for line in iter(read_line, ‘quit‘):
print(f‘You said: {line}‘)
print(‘Goodbye‘)
This avoids while True altogether. It is less flexible, but it is very clean for simple cases.
2) Generators and yield for Long-Running Pipelines
If your loop is part of a pipeline, a generator can be a better fit:
def events(stream):
for line in stream:
if not line.strip():
continue
yield line.strip()
for event in events(open(‘events.log‘)):
print(event)
Here, the loop lives inside the generator, and the caller drives it with a for loop. This makes your code more testable and composable.
3) while condition for Known State
If you already have a clear condition, just use it. It makes the code’s intent obvious:
remaining = 3
while remaining > 0:
remaining -= 1
My rule of thumb: if you can express the exit condition in one line without awkward variables or double negatives, do it.
Practical Scenario: Socket Server Loop
Network servers are a classic place for while True. Here is a minimal TCP server loop that shows the core pattern and a clean shutdown path:
import socket
HOST = ‘127.0.0.1‘
PORT = 5000
server = socket.socket(socket.AFINET, socket.SOCKSTREAM)
server.setsockopt(socket.SOLSOCKET, socket.SOREUSEADDR, 1)
server.bind((HOST, PORT))
server.listen()
server.settimeout(1.0)
shutdown = False
try:
while True:
if shutdown:
break
try:
conn, addr = server.accept()
except socket.timeout:
continue
with conn:
data = conn.recv(1024)
if not data:
continue
conn.sendall(data)
finally:
server.close()
Key points I rely on here:
settimeoutkeeps the loop responsive so it can exit.with connensures the socket closes even on exceptions.- The loop does not busy-wait because
accept()blocks until timeout.
Practical Scenario: File Watcher With Backoff
Polling the filesystem is a common task in small scripts. Here is a straightforward pattern with an adaptive sleep that reduces load when nothing changes:
import os
import time
path = ‘data.txt‘
last_size = None
sleep_time = 0.1
max_sleep = 2.0
while True:
try:
size = os.path.getsize(path)
except FileNotFoundError:
size = None
if size != last_size:
print(‘Change detected‘)
last_size = size
sleep_time = 0.1
else:
sleeptime = min(maxsleep, sleep_time * 1.5)
time.sleep(sleep_time)
This loop still uses while True, but it behaves responsibly. The backoff keeps CPU usage low while staying reasonably responsive when the file changes.
Retry Loops With Exponential Backoff and Jitter
Basic retries are easy, but production retries should be polite. Exponential backoff avoids hammering dependencies. Jitter spreads out retries when many clients fail at the same time.
import random
import time
attempt = 0
max_attempts = 6
base = 0.2
while True:
attempt += 1
ok = False # Replace with real operation
if ok:
print(‘Success‘)
break
if attempt >= max_attempts:
print(‘Failed after retries‘)
break
backoff = base (2 * (attempt - 1))
jitter = random.uniform(0, backoff * 0.1)
time.sleep(backoff + jitter)
This is a version I actually ship. It keeps retries under control and protects downstream systems.
while True in Async Code
If you use asyncio, you still use while True, but you must be cancellation-friendly and you must await inside the loop or it will block the event loop.
import asyncio
async def worker(queue, stop_event):
while True:
if stopevent.isset():
break
try:
job = await asyncio.wait_for(queue.get(), timeout=0.5)
except asyncio.TimeoutError:
continue
try:
# process job
pass
finally:
queue.task_done()
async def main():
queue = asyncio.Queue()
stop_event = asyncio.Event()
task = asyncio.createtask(worker(queue, stopevent))
await asyncio.sleep(2)
stop_event.set()
await task
asyncio.run(main())
Two rules I stick to in async loops:
- Always
awaitin the loop body so you do not block other tasks. - Handle cancellation or stop events so tasks can exit cleanly.
Threading and Stop Events
If you are running a loop in a thread, a threading.Event is the cleanest way to signal shutdown. It is safer than a global boolean because it is thread-safe and expressive.
import threading
import time
stop_event = threading.Event()
def worker():
while True:
if stopevent.isset():
break
# do work
time.sleep(0.2)
thread = threading.Thread(target=worker)
thread.start()
Later
stop_event.set()
thread.join()
This pattern is reliable, simple, and testable.
Testing while True Loops Without Pain
Testing infinite loops is all about control. I avoid testing them as-is, and instead inject dependencies that allow the loop to stop.
1) Inject a stop condition
def run(get_command, stop):
while True:
if stop():
break
cmd = get_command()
if cmd == ‘quit‘:
break
In tests, I pass a stop function that returns True after a few calls.
2) Use iterables with a sentinel
def loop(commands):
it = iter(commands)
while True:
try:
cmd = next(it)
except StopIteration:
break
if cmd == ‘quit‘:
break
This turns your loop into a deterministic, testable unit.
3) Add a max iterations guard in test mode
def poll(getjob, maxiters=None):
iters = 0
while True:
job = get_job()
if job is None:
break
iters += 1
if maxiters is not None and iters >= maxiters:
break
I keep the guard optional so production behavior stays unchanged.
Debugging Infinite Loops
When a while True loop misbehaves, I start with these steps:
1) Add a counter and log every N iterations. This tells me if the loop is spinning too fast.
2) Log state transitions, not every iteration. I want to know why it is stuck, not drown in noise.
3) Check for blocking calls. A loop that appears hung may simply be blocked on I/O.
4) Add a temporary timeout to surface the issue. A quick timeout can reveal the code path that never returns.
A quick diagnostic pattern:
import time
start = time.monotonic()
count = 0
while True:
count += 1
if count % 1000 == 0:
elapsed = time.monotonic() - start
print(f‘Iterations: {count}, elapsed: {elapsed:.2f}s‘)
# work
if count >= 5000:
break
It is blunt, but it gets answers fast.
The Human Factors: Readability and Intent
I care about readability more than cleverness. When someone reads a while True loop, they should immediately see how it exits. I look for:
- A clear, visible
breaknear the top or after the primary work. continuestatements that are used to flatten logic, not to hide it.- Minimal mutable state spread across the loop body.
If the exit condition is buried under layers of branching, I refactor. Often that means extracting helper functions or using guard clauses.
A Simple Heuristic I Use
I ask myself one question: is the loop’s exit condition part of the work I do inside it? If the answer is yes, I use while True. If the answer is no, I write the condition in the loop header.
This keeps my code honest and helps other people understand why I chose an infinite loop in the first place.
Production Considerations: Observability and Safety
If a while True loop runs in production, I treat it like a little service. That means:
- Metrics: I track iterations, successes, failures, and latency in ranges, not exact numbers.
- Logging: I log only on state changes, retries, and errors.
- Timeouts: I use timeouts for external calls so the loop remains responsive.
- Backoff: I slow down when upstream dependencies are failing.
- Shutdown: I provide a clear exit path, usually a signal or flag.
A tiny example that combines several of these:
import time
sleep_time = 0.1
max_sleep = 2.0
while True:
ok = False # Replace with real check
if ok:
sleep_time = 0.1
else:
sleeptime = min(maxsleep, sleep_time * 2)
time.sleep(sleep_time)
I keep it simple and predictable. The goal is not perfect efficiency; the goal is stability.
A Checklist Before I Ship a while True Loop
I do a quick pass with this list:
- Is there at least one obvious
breakorreturn? - Can the loop respond to shutdown signals or stop events?
- Does it avoid busy-waiting (no hot spinning)?
- Are exceptions handled intentionally (no silent failures)?
- Are resources cleaned up when the loop exits?
- Is the logging volume sane for long runtimes?
- Can I test it without waiting forever?
If I can answer yes to all of these, I am comfortable shipping the loop.
A Quick Comparison Table: Use vs Avoid
while True when… Avoid while True when…
—
A single condition can go in the while header
You have a fixed number of iterations
A for loop over data is clearer
You can express the state cleanly with a boolean conditionI keep this table in my head when choosing loop styles.
A Realistic End-to-End Example: Health Check Worker
This example is longer, but it is the kind of code I actually ship. It runs indefinitely, checks a resource, backs off when it fails, and exits cleanly when asked.
import time
shutdown = False
def check_service():
# Replace with real check
return True
sleep_time = 0.2
max_sleep = 3.0
while True:
if shutdown:
print(‘Stopping worker‘)
break
ok = check_service()
if ok:
print(‘Service healthy‘)
sleep_time = 0.2
else:
print(‘Service unhealthy‘)
sleeptime = min(maxsleep, sleep_time * 2)
time.sleep(sleep_time)
This loop is simple, but it has all the essentials: an exit path, adaptive delay, and a clear work step.
Common Questions I Get About while True
Is while True bad style?
No. It is bad style only when it hides the exit condition or creates an infinite loop by mistake. When the exit condition belongs inside the loop, while True is one of the clearest options.
Is it slow?
The loop itself is not slow. The work inside it is what matters. A loop that spins without waiting will burn CPU; a loop that blocks on I/O or sleeps is usually fine.
Can I always replace it with a for loop?
Sometimes. If you can express the loop as a sequence of known steps, a for loop is often better. If the exit condition is dependent on dynamic internal checks, I stick with while True.
What about KeyboardInterrupt?
If you want to allow Ctrl+C to stop the loop, catch KeyboardInterrupt and break cleanly. Keep it minimal so you do not swallow unexpected exceptions.
try:
while True:
# work
pass
except KeyboardInterrupt:
print(‘Interrupted by user‘)
Closing Thoughts
while True is not a hack. It is a straightforward pattern that gives you full control over when and why a loop ends. Used well, it makes intent obvious and keeps logic local. Used carelessly, it can create infinite spins, hide bugs, or burn resources.
When I write while True, I commit to three things: a clear exit path, responsible resource use, and predictable behavior under failure. If I cannot do that, I choose a different loop. That is the mindset I want you to take into your own code.
If you treat infinite loops as a tool with rules, you will find they are one of the most practical and readable patterns in Python.


