aio-libs/aiohttp · error · ValueError

Gunicorn's style options in form of `%(name)s` are not…

Error message

Gunicorn's style options in form of `%(name)s` are not supported for the log formatting. Please use aiohttp's format specification to configure access log formatting: http://docs.aiohttp.org/en/stable/logging.html#format-specification

What it means

Raised as a ValueError by GunicornWebWorker._get_valid_log_format() when the access-log format string contains a Gunicorn-style `%(name)s` placeholder. aiohttp's AccessLogger uses its own %-spec (e.g. %a %t %r %s %b), not Gunicorn's %(name)s dictionary form, so the worker rejects the incompatible format at startup. The exception bubbles out of _run() and aborts worker boot.

Solutions

  1. Use aiohttp's format specification: e.g. `%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"` (see http://docs.aiohttp.org/en/stable/logging.html#format-specification).
  2. Remove the custom access_log_format to fall back to the aiohttp default.
  3. If you only had Gunicorn's default format string, the worker auto-translates DEFAULT_GUNICORN_LOG_FORMAT to DEFAULT_AIOHTTP_LOG_FORMAT, so leaving it unset is safest.

Example fix

// before
# gunicorn.conf.py
access_log_format = '%(h)s %(l)s %(t)s "%(r)s" %(s)s %(b)s'

// after
access_log_format = '%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
# or simply omit it to use the aiohttp default
Defensive patterns

Strategy: validation

Validate before calling

import re
assert not re.search(r'%\([^)]+\)', access_log_format), \
    'use aiohttp access log format, not gunicorn %(name)s'

Type guard

def is_aiohttp_log_format(fmt: str) -> bool:
    import re
    return not re.search(r'%\([^)]+\)s', fmt)

Try / catch

# raised at worker boot as ValueError; fix the config rather than catching.
# Validate config before deploy:
try:
    assert is_aiohttp_log_format(cfg.access_log_format)
except AssertionError:
    cfg.access_log_format = '%a %t "%r" %s %b'

Prevention

When it happens

Trigger: Passing `--access-logformat '%(h)s %(l)s %(t)s %(r)s %(s)s %(b)s'` (a Gunicorn default-style string that is not exactly DEFAULT_GUNICORN_LOG_FORMAT) to the aiohttp gunicorn worker, or setting `access_log_format` in a gunicorn config file using %(name)s placeholders.

Common situations: Copying a gunicorn access_log_format from a Flask/Django deployment into an aiohttp config; a config file that worked under the sync gunicorn worker but is now pointed at GunicornWebWorker.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/98132f6e50dd6201. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/worker.py:238

        See ssl.SSLSocket.__init__ for more details.
        """
        if ssl is None:  # pragma: no cover
            raise RuntimeError("SSL is not supported.")

        ctx = ssl.SSLContext(cfg.ssl_version)
        ctx.load_cert_chain(cfg.certfile, cfg.keyfile)
        ctx.verify_mode = cfg.cert_reqs
        if cfg.ca_certs:
            ctx.load_verify_locations(cfg.ca_certs)
        if cfg.ciphers:
            ctx.set_ciphers(cfg.ciphers)
        return ctx

    def _get_valid_log_format(self, source_format: str) -> str:
        if source_format == self.DEFAULT_GUNICORN_LOG_FORMAT:
            return self.DEFAULT_AIOHTTP_LOG_FORMAT
        elif re.search(r"%\([^\)]+\)", source_format):
            raise ValueError(
                "Gunicorn's style options in form of `%(name)s` are not "
                "supported for the log formatting. Please use aiohttp's "
                "format specification to configure access log formatting: "
                "http://docs.aiohttp.org/en/stable/logging.html"
                "#format-specification"
            )
        else:
            return source_format


class GunicornUVLoopWebWorker(GunicornWebWorker):
    def init_process(self) -> None:
        import uvloop

        asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

        super().init_process()

View on GitHub (pinned to d041d4d0fd)