logging TypeError: not all arguments converted (tested)

--- Logging error --- followed by TypeError: not all arguments converted during string formatting means a log call passed arguments that don't match its % placeholders. We measured each variant on Python 3.13.5, including when the message disappears silently.

1. Passing arguments the print() way

import logging
logging.basicConfig(format="%(levelname)s %(message)s")
user, n = "ann", 3
logging.warning("user", user)              # print()-style: extra argument, no %s
print("--- the program keeps running ---")

Output:

--- Logging error ---
Traceback (most recent call last):
  File "/usr/lib/python3.13/logging/__init__.py", line 1151, in emit
    msg = self.format(record)
  File "/usr/lib/python3.13/logging/__init__.py", line 999, in format
    return fmt.format(record)
           ~~~~~~~~~~^^^^^^^^
  File "/usr/lib/python3.13/logging/__init__.py", line 712, in format
    record.message = record.getMessage()
                     ~~~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3.13/logging/__init__.py", line 400, in getMessage
    msg = msg % self.args
          ~~~~^~~~~~~~~~~
TypeError: not all arguments converted during string formatting
Call stack:
  File "/tmp/la/b1.py", line 4, in <module>
    logging.warning("user", user)              # print()-style: extra argument, no %s
Message: 'user'
Arguments: ('ann',)
--- the program keeps running ---

Measured on Python 3.13.5: logging formats the message lazily with msg % args. "user" has no %s for "ann", so the % fails. logging catches the error inside the handler, prints a --- Logging error --- block to stderr, and drops the record, while the program carries on. The Call stack and Message/Arguments lines at the bottom point to the bad call; the top of the traceback is logging's own code.

2. Too few arguments, or {} placeholders

import logging
logging.basicConfig(format="%(levelname)s %(message)s")
logging.warning("user %s has %d items", "ann")    # too few arguments
logging.warning("user {} has {} items", "ann", 3) # str.format placeholders
print("--- still running ---")

Output:

--- Logging error ---
Traceback (most recent call last):
  File "/usr/lib/python3.13/logging/__init__.py", line 1151, in emit
    msg = self.format(record)
  File "/usr/lib/python3.13/logging/__init__.py", line 999, in format
    return fmt.format(record)
           ~~~~~~~~~~^^^^^^^^
  File "/usr/lib/python3.13/logging/__init__.py", line 712, in format
    record.message = record.getMessage()
                     ~~~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3.13/logging/__init__.py", line 400, in getMessage
    msg = msg % self.args
          ~~~~^~~~~~~~~~~
TypeError: not enough arguments for format string
Call stack:
  File "/tmp/la/b2.py", line 3, in <module>
    logging.warning("user %s has %d items", "ann")    # too few arguments
Message: 'user %s has %d items'
Arguments: ('ann',)
--- Logging error ---
Traceback (most recent call last):
  File "/usr/lib/python3.13/logging/__init__.py", line 1151, in emit
    msg = self.format(record)
  File "/usr/lib/python3.13/logging/__init__.py", line 999, in format
    return fmt.format(record)
           ~~~~~~~~~~^^^^^^^^
  File "/usr/lib/python3.13/logging/__init__.py", line 712, in format
    record.message = record.getMessage()
                     ~~~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3.13/logging/__init__.py", line 400, in getMessage
    msg = msg % self.args
          ~~~~^~~~~~~~~~~
TypeError: not all arguments converted during string formatting
Call stack:
  File "/tmp/la/b2.py", line 4, in <module>
    logging.warning("user {} has {} items", "ann", 3) # str.format placeholders
Message: 'user {} has {} items'
Arguments: ('ann', 3)
--- still running ---

Measured: too few arguments gives not enough arguments for format string; {} placeholders (str.format style) with arguments give not all arguments converted, since logging's message format is %-style regardless of the style= you pass to basicConfig (that only affects the format string for the record layout).

3. Fix: %s placeholders, %% for a literal percent

import logging
logging.basicConfig(format="%(levelname)s %(message)s")
logging.warning("user %s has %d items", "ann", 3)        # fix: %-style placeholders
logging.warning("progress: 100%% done for %s", "ann")     # a literal % must be %%
logging.warning(f"f-string is fine too: {'ann'} 100%")   # no args -> no formatting

Output:

WARNING user ann has 3 items
WARNING progress: 100% done for ann
WARNING f-string is fine too: ann 100%

Measured: one %s/%d per argument works. When you pass arguments, a literal percent sign must be written %%. With no arguments, logging doesn't format at all, so an f-string containing 100% is printed as is. f-strings work but are formatted even when the level is disabled; %s with arguments is formatted only if the record is emitted.

4. In production it can vanish completely

import logging
logging.raiseExceptions = False      # production setting: errors are silently dropped
logging.basicConfig(format="%(levelname)s %(message)s")
logging.warning("user", "ann")
print("--- nothing was logged, and no error shown ---")

Output:

--- nothing was logged, and no error shown ---

Measured: with logging.raiseExceptions = False (a setting some applications use in production), the same mistake prints nothing at all: no message, no error. If a log line you expect is missing, check its arguments.

Frequently asked questions

What does 'not all arguments converted during string formatting' mean in logging?

The message has fewer % placeholders than the arguments you passed, e.g. logging.info('user', name). We measured logging printing a '--- Logging error ---' block and dropping the record on Python 3.13.5.

Can I use {} placeholders in logging messages?

Not with arguments: the message is always formatted with %. We measured 'user {} has {} items' with two args failing. Use %s, or an f-string with no args.

Does a logging error crash my program?

No. logging catches it, prints the traceback to stderr and drops the message; we measured the program continuing. With logging.raiseExceptions = False, nothing is printed at all.

How do I log a literal percent sign?

Write %% when you pass arguments. With no arguments, a single % is fine; we measured both.

Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5 on Linux: print-style logging arguments, too few arguments, {} placeholders, %s and %% fixes, f-strings without arguments, and logging.raiseExceptions = False. Every block was run verbatim and its output pasted from that run (stdout and stderr together). PlainHeaders is our extension; everything else here is the standard library.