At a glance
- tqdm writes to stderr, so
> out.logcaptures your prints but not the bar; add2>&1. - In a log file every refresh is saved as text: use
TQDM_MININTERVAL=30ordisable=None. - Use
tqdm.write()instead ofprint()inside the loop. - No percentage means no
len(): useenumerate(tqdm(x)),zip(tqdm(a), b)ortotal=.
Find your symptom
Start with the row that matches what you see:
| Symptom | Cause | Fix |
|---|---|---|
Bar missing from out.log | tqdm writes to stderr and > only redirects stdout | Add 2>&1, or pass file=sys.stdout |
| Log file fills with copies of the bar | Every refresh is saved as text | TQDM_MININTERVAL=30, mininterval=30 or disable=None |
| New line on every update | print() inside the loop | tqdm.write() |
| New lines in PyCharm's Run window | The standard output console is not a full terminal | Turn on "Emulate terminal in output console" |
| Count but no percentage or ETA | The iterable has no len() | total=len(x) or enumerate(tqdm(x)) |
| Bar appears only after a few seconds | delay=N is set | Lower or remove delay |
| Nested bars pile up | Inner bars keep the default leave=True | position=n and leave=False |
IProgress not found in Jupyter | ipywidgets is missing in the kernel | %pip install ipywidgets, then restart the kernel |
| Broken block characters on Windows | The console only partly supports Unicode | ascii=True |
UnicodeEncodeError: 'charmap' codec | tqdm 4.68.0 or 4.68.1 | Upgrade, or pin tqdm!=4.68.0,!=4.68.1 |
No bar in logs, nohup, Docker or CI
By default, tqdm writes to stderr. If you run python script.py > out.log, you're only redirecting stdout, so the progress bar stays on your terminal instead of going into the file. To send both streams to the same place, add 2>&1 to your command. nohup does this for you when you start it from a terminal: both streams go to nohup.out.
If you don't want bars in your log files at all, pass disable=None to tqdm(). That tells tqdm to check if the output is a real terminal (TTY) and turn the bar off when it isn't, while disable=True turns it off everywhere. You can also set the environment variable TQDM_DISABLE=1 to kill all bars without touching your code.
When you do keep bars in logs or CI, remember how tqdm redraws: each refresh writes a carriage return plus a fresh copy of the bar. A terminal redraws the line in place, but a log file keeps every copy on one long line that grows fast, and some CI log viewers show each copy as a new line. A few ways to calm things down:
- Set
mininterval=30in Python orTQDM_MININTERVAL=30in your environment so the bar updates every 30 seconds instead of the default 0.1 seconds. In our test, a 2-second loop wrote 22 refreshes by default and 2 withTQDM_MININTERVAL=30. - For cloud log consoles like CloudWatch or Kubernetes, tqdm's FAQ suggests
export TQDM_POSITION=-1. - With Docker Compose, tqdm's FAQ notes that
docker-compose upshows no intermediate output; usedocker-compose runinstead, and settty: truein your configuration.
# bar and output in one file, one refresh every 30 seconds
TQDM_MININTERVAL=30 python train.py > train.log 2>&1
# no bars at all
TQDM_DISABLE=1 python train.py > train.log 2>&1A new line on every update
A regular print() statement inside your loop writes a full line to stdout, which breaks the bar line that tqdm is trying to hold in place on stderr. Switch print() to tqdm.write(), which prints your text above the bar and leaves the progress line intact.
from time import sleep
from tqdm import tqdm
for job in tqdm(range(5), desc="jobs"):
sleep(0.2)
tqdm.write(f"done with job {job}")Two other things will cause a fresh line on every tick even without print():
- Consoles with limited cursor support: nested bars need a console that can move the cursor up a line, and tqdm's FAQ points out that IDLE, ConEmu and PyCharm lack full support for this. In PyCharm, turn on Emulate terminal in output console in the run configuration.
- Windows consoles and Unicode: on Windows, tqdm installs
coloramato keep nested bars on their own lines, and consoles that only partly support Unicode often needascii=Trueto avoid broken block characters. If you're pinned to tqdm 4.68.0 or 4.68.1, those versions accidentally changed theasciidefault and could raiseUnicodeEncodeError: 'charmap' codec can't encode characters; 4.68.2 reverted it.
Bar stuck at 0%, no percentage, or no ETA
If your bar shows an iteration count and speed without a percentage bar or an ETA, tqdm doesn't know the total length of what you're looping over. Generators don't have a len(), and wrapping a list in enumerate() or zip() before passing it to tqdm hides the length too.
If you write tqdm(enumerate(x)), tqdm sees an enumerate object with no length. Flip the order to enumerate(tqdm(x)) so tqdm wraps the sequence directly, or pass total=len(x) yourself. Do the same thing with zip: write zip(tqdm(a), b) instead of wrapping the whole zip call.
from tqdm import tqdm
names = ["a", "b", "c"]
sizes = [10, 20, 30]
for i, name in enumerate(tqdm(names)):
pass
for name, size in zip(tqdm(names), sizes):
pass
def rows():
yield from range(1_000)
for row in tqdm(rows(), total=1_000):
passIf the bar doesn't show up at all for the first part of a run and then suddenly appears, check if delay=N is set, since that hides the bar for the first N seconds (the default is 0). Redraw timing is also controlled by miniters, mininterval (default 0.1 seconds), and maxinterval (which defaults to 10 seconds).
Notebook bars that do not render
In Jupyter notebooks, tqdm.notebook needs ipywidgets installed to draw the widget bar. Without it, you'll hit an IProgress not found error and the bar won't render.
Either install ipywidgets in that kernel's environment, or import with from tqdm.auto import tqdm, which falls back to a standard text bar when the widget isn't there. The Jupyter guide covers JupyterLab and VS Code.
Nested and parallel bars that overwrite each other
By default, tqdm sets leave=True, which keeps finished bars on screen when their loop ends. When you have an outer loop and an inner loop running together, completed inner bars pile up or collide with the outer bar unless you pin their lines.
Give each bar its own line with position=n (starting at 0 for the outer bar, 1 for the inner bar), and pass leave=False on the inner bar so it clears itself when each inner loop finishes.
from time import sleep
from tqdm import tqdm
for epoch in tqdm(range(3), desc="epochs", position=0):
for batch in tqdm(range(50), desc="batches", position=1, leave=False):
sleep(0.01)Check progress when you cannot see the terminal
When no terminal is attached (nohup, a container, a redirected log), Runpeek lets you view your existing tqdm bar in any browser through the tqdm-live Python package. Swap from tqdm import tqdm for from tqdm_live import tqdm, or call tqdm_live.patch() once before any bar is created, and each bar prints a runpeek ▶ https://runpeek.app/b/<uuid> link.
That line goes to stdout, which Python buffers when it writes to a file, so set PYTHONUNBUFFERED=1 to see it in the log straight away. Links need no account and expire 30 minutes after the last update.
FAQ
Why does tqdm print to stderr?
tqdm writes to stderr by default so your script's actual output on stdout stays clean. If you redirect a script with python script.py > out.log, your data goes into out.log while the progress bar still shows up on your terminal; use 2>&1 if you want both sent to the same place.
How do I stop tqdm from filling my log file?
Each refresh writes a fresh copy of the bar, and a log file keeps all of them. Pass disable=None to turn the bar off when the output isn't a terminal, or set mininterval=30 in code or TQDM_MININTERVAL=30 in your environment to refresh every 30 seconds instead of every 0.1.
How do I disable tqdm without changing code?
Set the environment variable TQDM_DISABLE=1 before running your script. Environment variable overrides have been supported since tqdm 4.66.0, and TQDM_DISABLE=1 turns all bars off without touching your Python files.
Why does tqdm work in the terminal but not in PyCharm?
PyCharm's standard output console isn't a full terminal, and tqdm's FAQ lists PyCharm, IDLE and ConEmu as lacking full support for moving the cursor up a line. Turn on "Emulate terminal in output console" in the run configuration, which JetBrains recommends for scripts that redraw a line with carriage returns.
Why does my bar only appear when the loop ends?
If you're using docker-compose up, tqdm's FAQ notes that it hides intermediate output; switch to docker-compose run with tty: true. Inside Python, check whether delay=N is set on the bar, or whether a large miniters or mininterval value is holding back redraws until the loop finishes.
Check on long runs from your phone
Change one import and every tqdm bar prints a link you can open on any device. Get a Discord or Slack message when the run finishes, fails or stalls.
pip install tqdm-liveSources
- tqdm README: FAQ and known issues github.com
- tqdm API documentation tqdm.github.io
- tqdm 4.68.2 release notes github.com
- PyCharm Python run configuration jetbrains.com
- nohup manual page man7.org