python/cpython · error · TypeError

must be str, not

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').

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.

Appendix: 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)