Skip to content

Add RFC 9457 Problem Details support for Simple API errors #19371

Draft
luisgcoding wants to merge 8 commits intopypi:mainfrom
luisgcoding:rfc9457-add-problem-details
Draft

Add RFC 9457 Problem Details support for Simple API errors #19371
luisgcoding wants to merge 8 commits intopypi:mainfrom
luisgcoding:rfc9457-add-problem-details

Conversation

@luisgcoding
Copy link

@luisgcoding luisgcoding commented Jan 23, 2026

This PR implements RFC9457 support for PyPI's Simple API error responses, replacing plain text error messages with standardized, machine-readable JSON error responses.

This is part of a Pre-PEP work discussed here.

Current PyPi behaviour when executing command curl -i http://localhost:80/simple/nonexistent-package/ :
Screenshot 2026-01-28 at 7 01 43 PM

Now with the change proposed here:
Screenshot 2026-01-28 at 6 48 02 PM

detail: str | None = None
type: str = "about:blank"
instance: str | None = None
extensions: dict[str, Any] = field(default_factory=dict)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I was looking at the test, it seemed odd that extensions were passed in as a dictionary, but got flattened in the JSON result. I was put off by the different shape, but now I see why; it's because ProblemDetails is a dataclass, which doesn't have a way to specify **kws. I think that's okay, except that the way it's written, it's possible for keys in extensions to override the required arguments, e.g.

pd = ProblemDetails(400, "Bad request", extensions={"status": 200})

Maybe that's not terrible, but maybe it makes sense to do some extra checking before the result.update() on line 66?

I also wonder is whether extensions should be a kw_only field?

Copy link
Author

@luisgcoding luisgcoding Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added validation and made the field kw_only here.

"title": self.title,
}

if self.type != "about:blank":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reading of about:blank says it can be implicit but doesn't need to be. Should we default to explicit?

Also should we support the SHOULD "the title SHOULD be the same as the recommended HTTP status phrase for that code"?

Copy link
Author

@luisgcoding luisgcoding Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the SHOULD here in method problem_details_from_exception. I think is better to be implicit but happy to change it otherwise.

def problem_details_from_exception(
exc: HTTPException, detail: str | None = None, **extensions: Any
) -> ProblemDetails:
problem_detail = detail
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be:

    if detail is None:
        problem_detail = getattr(...
    else:
        problem_detail = detail

or a ternary expression, but I don't think that would make things more readable.

Copy link
Author

@luisgcoding luisgcoding Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed here



def accepts_problem_json(request) -> bool:
acceptable = request.accept.acceptable_offers([RFC9457_CONTENT_TYPE])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was some question on DPO whether we wanted to limit this response to only requests that explicitly accept application/problem+json, although now that I'm looking I don't see accepts_problem_json() called anywhere.

I'm unsure so maybe @woodruffw can chime in?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the accepts_problem_json for the moment. We can add it back if we decide to use it for backwards compatibility.

@luisgcoding luisgcoding force-pushed the rfc9457-add-problem-details branch from ebcbecd to 7bcb466 Compare January 28, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants