python/cpython · error · TypeError

must be str, not %s

Error message

must be str, not %s

What it means

Raised by date.__format__ when the format specification passed to format(a_date, fmt) or an f-string is not a str instance. The datetime module only accepts string format specs; passing bytes or any other type is rejected immediately before strftime is called. The message names the actual type received (e.g. 'bytes', 'int').

Source

Thrown at Lib/_pydatetime.py:1124

        weekday = self.toordinal() % 7 or 7
        return "%s %s %2d 00:00:00 %04d" % (
            _DAYNAMES[weekday],
            _MONTHNAMES[self._month],
            self._day, self._year)

    def strftime(self, format):
        """
        Format using strftime().

        Example: "%d/%m/%Y, %H:%M:%S"
        For a list of supported format codes, see the documentation:
            https://docs.python.org/3/library/datetime.html#format-codes
        """
        return _wrap_strftime(self, format, self.timetuple())

    def __format__(self, fmt):
        if not isinstance(fmt, str):
            raise TypeError("must be str, not %s" % type(fmt).__name__)
        if len(fmt) != 0:
            return self.strftime(fmt)
        return str(self)

    def isoformat(self):
        """Return the date formatted according to ISO.

        This is 'YYYY-MM-DD'.

        References:
        - https://www.w3.org/TR/NOTE-datetime
        - https://www.cl.cam.ac.uk/~mgk25/iso-time.html
        """
        return "%04d-%02d-%02d" % (self._year, self._month, self._day)

    __str__ = isoformat

    # Read-only field accessors

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Decode the format spec to str before formatting: fmt.decode('utf-8') if it is bytes
  2. Pass a literal str format spec, e.g. format(d, '%Y-%m-%d') or f"{d:%Y-%m-%d}"
  3. Default to str(d) when the spec variable may be None or non-string

Example fix

// before
fmt = b'%Y-%m-%d'
s = format(d, fmt)

// after
fmt = '%Y-%m-%d'
s = format(d, fmt)
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(fmt, str):
    fmt = fmt.decode('utf-8') if isinstance(fmt, (bytes, bytearray)) else str(d)
s = format(d, fmt)

Type guard

def is_str_spec(fmt) -> bool:
    return isinstance(fmt, str)

Try / catch

try:
    s = format(d, fmt)
except TypeError:
    s = str(d)  # or re-raise after logging the spec type

Prevention

When it happens

Trigger: format(date(2024,1,1), b'%Y-%m-%d'); f"{d:{fmt}}" where fmt is bytes (e.g. read from a file or config without decoding); format(some_date, None); calling format(date_obj, 42).

Common situations: Format strings loaded from binary files, network payloads, or environ/os.environb values that are bytes; passing a variable that was never decoded; programmatically building format specs from non-string data.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/1b7ec5f2c33d5524. Report an issue: GitHub.