Skip to content

[BUG]: TypeError: not all arguments converted during string formatting #13211

Open
@AndyYaG

Description

@AndyYaG

Tracer Version(s)

3.4.1

Python Version(s)

Python 3.12.0

Pip Version(s)

pip 23.2.1

Bug Report

String Formatting Issue in DDTelemetryLogHandler

Issue Summary

A TypeError: not all arguments converted during string formatting exception occurred in the DDTelemetryLogHandler.emit() method when attempting to format log messages using the % operator with record.msg and record.args:

self.telemetry_writer.add_error(1, record.msg % record.args, full_file_name, record.lineno)

This issue happened when I updated the following libraries:

  • Python 3.9.13 -> 3.12.0
  • Django 4.1.9 -> 5.2
  • ddtrace 2.12.2 -> 3.4.1

Root Cause Analysis

The error occurs because of a mismatch between format specifiers in record.msg and the arguments in record.args. This mismatch can happen in several scenarios:

  1. Tuple Handling: When record.args is already a tuple, direct application using % can cause errors because the tuple is not unpacked correctly.

  2. Format Specifier Mismatch: If there are more or fewer format specifiers (%s, %d, etc.) in the message than there are arguments provided.

  3. Missing Arguments: When a format specifier exists in the message, but no corresponding argument is provided.

  4. Non-Iterable Value: If record.args is a non-iterable value when multiple format specifiers exist in record.msg

  5. Arguments Mismatch: If there's a mismatch between the number of specifiers and provided arguments

  6. Unexpected type: If record.args is an unexpected type that causes issues with the formatting mechanism

Solution Implemented

The issue was resolved by replacing the direct formatting with the LogRecord.getMessage() method:

self.telemetry_writer.add_error(1, record.getMessage(), full_file_name, record.lineno)
def getMessage(self):
        """
        Return the message for this LogRecord.

        Return the message for this LogRecord after merging any user-supplied
        arguments with the message.
        """
        msg = str(self.msg)
        if self.args:
            msg = msg % self.args
        return msg

This method internally manages all string formatting edge cases and safely returns a properly formatted log message. If using Python 3.12, the getMessage method is already available in the standard logging package. However, a more robust solution should ensure consistent handling of these edge cases across different Python versions. I believe we can approach it with something like this:

try:
    iter(possibly_iterable)
except TypeError:
    # Handle non-iterable case
    iterable = [possibly_iterable]
else:
    # Handle iterable case
    iterable = possibly_iterable

Or could we implement a solution to maintain multiple versions for compatibility?

Reproduction Code

No response

Error Logs

local-gameserver | Traceback (most recent call last):
local-gameserver | File "/usr/local/lib/python3.12/threading.py", line 1052, in _bootstrap_inner
local-gameserver | self.run()
local-gameserver | File "/usr/local/lib/python3.12/threading.py", line 989, in run
local-gameserver | self._target(*self._args, **self._kwargs)
local-gameserver | File "/usr/local/lib/python3.12/site-packages/concurrent_log_handler/queue.py", line 54, in _monitor
local-gameserver | super()._monitor() # type: ignore[misc]
local-gameserver | ^^^^^^^^^^^^^^^^^^
local-gameserver | File "/usr/local/lib/python3.12/logging/handlers.py", line 1585, in _monitor
local-gameserver | self.handle(record)
local-gameserver | File "/usr/local/lib/python3.12/logging/handlers.py", line 1566, in handle
local-gameserver | handler.handle(record)
local-gameserver | File "/usr/local/lib/python3.12/logging/init.py", line 1028, in handle
local-gameserver | self.emit(record)
local-gameserver | File "/usr/local/lib/python3.12/site-packages/ddtrace/internal/telemetry/logging.py", line 24, in emit
local-gameserver | self.telemetry_writer.add_error(1, record.msg % record.args, full_file_name, record.lineno)
local-gameserver | ~~~~~~~~~~~^~~~~~~~~~~~~
local-gameserver | TypeError: not all arguments converted during string formatting

Libraries in Use

aiohappyeyeballs==2.6.1
aiohttp==3.11.16
aiosignal==1.3.2
amqp==5.3.1
anyio==4.9.0
asgiref==3.8.1
attrs==25.3.0
bcrypt==4.3.0
billiard==4.2.1
boto3==1.37.34
botocore==1.37.34
bytecode==0.16.2
cachetools==5.5.2
cbor2==5.6.5
celery==5.5.1
celery-redbeat==2.3.2
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.1
click==8.1.8
click-didyoumean==0.3.1
click-plugins==1.1.1
click-repl==0.3.0
concurrent-log-handler==0.9.25
cookies==2.2.1
cryptography==44.0.2
datadog==0.51.0
ddtrace==3.4.1
decorator==5.2.1
defusedxml==0.7.1
Deprecated==1.2.18
distlib==0.3.9
Django==5.2
django-annoying==0.10.8
django-datetime-widget==0.9.3
django-extensions==4.1
django-ipware==7.0.1
django-jenkins==0.110.0
django-ratelimit3==1.0.1
django-redis==5.4.0
django-suit==0.2.28
django-tables2==2.7.5
django-uuidfield==0.5.0
envier==0.6.1
filelock==3.18.0
flatbuffers==25.2.10
flower==2.0.1
frozenlist==1.5.0
gevent==24.11.1
google-api-core==2.24.2
google-api-python-client==2.167.0
google-auth==2.39.0
google-auth-httplib2==0.2.0
google-cloud-core==2.4.3
google-cloud-storage==3.1.0
google-crc32c==1.7.1
google-resumable-media==2.7.2
googleapis-common-protos==1.70.0
greenlet==3.2.0
h11==0.14.0
h2==4.2.0
hiredis==3.1.0
hpack==4.1.0
httpcore==1.0.8
httplib2==0.22.0
httpx==0.28.1
humanize==4.12.2
hyperframe==6.1.0
idna==3.10
importlib_metadata==8.6.1
ipaddress==1.0.23
jmespath==1.0.1
kombu==5.5.2
lazy-object-proxy==1.10.0
leaderboard==3.7.3
lxml==5.3.2
maxminddb==2.6.3
mock==5.2.0
multidict==6.4.3
ndg-httpsclient==0.5.1
numpy==2.2.4
oauth2client==4.1.3
oauthlib==3.2.2
opentelemetry-api==1.32.1
pandas==2.2.3
platformdirs==4.3.7
portalocker==3.1.1
prometheus_client==0.21.1
prompt_toolkit==3.0.51
propcache==0.3.1
proto-plus==1.26.1
protobuf==6.30.2
pubnub==10.3.0
pyasn1==0.6.1
pyasn1_modules==0.4.2
pycparser==2.22
pycryptodome==3.22.0
pycryptodomex==3.22.0
PyJWT==2.10.1
pylibmc==1.6.3
pynamodb==6.0.2
pyOpenSSL==25.0.0
pyparsing==3.2.3
python-dateutil==2.9.0.post0
python-ipware==3.0.0
python-oauth2==1.1.1
python-redis-lock==4.0.0
python-social-auth==0.3.6
python3-openid==3.2.0
pytz==2025.2
qless-py==0.11.4
redis==5.2.1
requests==2.32.3
requests-oauthlib==2.0.0
responses==0.4.0
rsa==4.9
s3transfer==0.11.4
setproctitle==1.3.5
setuptools==29.0.1
simplejson==3.20.1
six==1.17.0
sniffio==1.3.1
social-auth-app-django==5.4.3
social-auth-core==4.5.6
South==1.0.2
sqlparse==0.5.3
tenacity==9.1.2
theine==0.5.0
theine_core==0.4.5
titan-sdk==3.0.9
tornado==6.4.2
typing_extensions==4.13.2
tzdata==2025.2
tzlocal==5.3.1
unittest-xml-reporting==3.2.0
uritemplate==4.1.1
urllib3==2.4.0
uwsgidecorators==1.1.0
vine==5.1.0
virtualenv==20.30.0
wcwidth==0.2.13
wheel==0.42.0
wrapt==1.17.2
xmltodict==0.14.2
yarl==1.19.0
zipp==3.21.0
zope.event==5.0
zope.interface==7.2

Operating System

Linux 6c8efc41b480 6.9.8-orbstack-00170-g7b4100b7ced4 #1 SMP Thu Jul 11 03:32:20 UTC 2024 aarch64 GNU/Linux

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions