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:
- Set a
template_name on the response so the BrowsableAPIRenderer can render it, or
- Exclude
BrowsableAPIRenderer from the view's renderer_classes, or
- 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
Summary
The PyPI simple index view returns HTTP 500 when DRF's
BrowsableAPIRendereris selected during content negotiation. This happens when clients request/api/pypi/<domain>/<path>/simple/<package>/without?format=json— the view returns aTemplateResponsewithout settingtemplate_name, causingBrowsableAPIRenderer.get_template_names()to raiseImproperlyConfigured.Requests to the same endpoints with
?format=jsonsucceed (HTTP 200).Error
Full traceback
Reproduction
Requests without
?format=jsonfail:Requests with
?format=jsonsucceed:Impact observed in production
uv(pip installsubcommand does not append?format=json;pip compiledoes),curl/api/pypi/public-rhai/rhoai/3.5/{variant}/simple/{package}/Expected behavior
The simple index view should either:
template_nameon the response so theBrowsableAPIRenderercan render it, orBrowsableAPIRendererfrom the view'srenderer_classes, orapplication/vnd.pypi.simple.v1+htmlresponseEnvironment
BrowsableAPIRendereris in the default renderer classes