Skip to content

PyPI simple API returns 500 when BrowsableAPIRenderer is selected (no template_name set) #1302

Description

@dkliban

Summary

The PyPI simple index view returns HTTP 500 when DRF's BrowsableAPIRenderer is selected during content negotiation. This happens when clients request /api/pypi/<domain>/<path>/simple/<package>/ without ?format=json — the view returns a TemplateResponse without setting template_name, causing BrowsableAPIRenderer.get_template_names() to raise ImproperlyConfigured.

Requests to the same endpoints with ?format=json succeed (HTTP 200).

Error

django.core.exceptions.ImproperlyConfigured: Returned a template response with no `template_name` attribute set on either the view or response

Full traceback

File "django/core/handlers/base.py", line 220, in _get_response
    response = response.render()
File "django/template/response.py", line 114, in render
    self.content = self.rendered_content
File "rest_framework/response.py", line 74, in rendered_content
    ret = renderer.render(self.data, accepted_media_type, context)
File "rest_framework/renderers.py", line 156, in render
    template_names = self.get_template_names(response, view)
File "rest_framework/renderers.py", line 188, in get_template_names
    raise ImproperlyConfigured(
django.core.exceptions.ImproperlyConfigured: Returned a template response with no `template_name` attribute set on either the view or response

Reproduction

Requests without ?format=json fail:

GET /api/pypi/<domain>/<path>/simple/<package>/ → 500

Requests with ?format=json succeed:

GET /api/pypi/<domain>/<path>/simple/<package>/?format=json → 200

Impact observed in production

  • 76 occurrences across 22 of 50 API pods in a ~30-minute window (2026-07-24)
  • A previous burst of 962 errors in ~35 minutes (2026-07-23) may have the same root cause
  • Affected clients: uv (pip install subcommand does not append ?format=json; pip compile does), curl
  • All affected URLs follow the pattern: /api/pypi/public-rhai/rhoai/3.5/{variant}/simple/{package}/

Expected behavior

The simple index view should either:

  1. Set a template_name on the response so the BrowsableAPIRenderer can render it, or
  2. Exclude BrowsableAPIRenderer from the view's renderer_classes, or
  3. Handle content negotiation so that non-JSON requests get a valid HTML or application/vnd.pypi.simple.v1+html response

Environment

  • pulp_python deployed on pulp-service (packages.redhat.com)
  • Django REST Framework BrowsableAPIRenderer is in the default renderer classes

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions