pub struct Problem { /* private fields */ }Expand description
An ACME error, rendered as an RFC 8555 §6.7 application/problem+json
document:
{ "type": "urn:ietf:params:acme:error:malformed", "detail": "…", "status": 400 }This struct represents ACME protocol errors that are returned to clients
in the standardized RFC 8555 problem+json format. It implements the IntoResponse
trait to convert errors into proper HTTP responses.
§ACME Protocol Compliance
Following RFC 8555, each error has:
- A
typefield with URN format identifying the error type - A
detailfield with human-readable description - A
statusfield with HTTP status code
§Error Types
malformed: Request format issues (HTTP 400)bad_nonce: Invalid or expired nonce (HTTP 400)unauthorized: Signature verification failures (HTTP 401)server_internal: Internal server errors (HTTP 500)account_does_not_exist: Referenced account not found (HTTP 400)order_not_ready: Finalize attempted on a non-readyorder (HTTP 403)bad_csr: Unacceptable finalize CSR (HTTP 400)unsupported_identifier: Unsupported newOrder identifier type (HTTP 400)rejected_identifier: Identifier refused by server policy (HTTP 403)access_denied: Request blocked by a filter (HTTP 403)external_account_required: newAccount missing a required EAB (HTTP 400)already_revoked: Certificate already revoked (HTTP 400)bad_revocation_reason: UnsupportedCRLReasoncode (HTTP 400)key_change_conflict: keyChange new key belongs to another account (HTTP 409)unsupported_media_type: body was notapplication/jose+json(HTTP 415)method_not_allowed: resource read with the wrong method, e.g. a bare GET (HTTP 405)not_found: nothing routed at this path (HTTP 404)
§Usage
Used both as the AcmeRequest extractor’s rejection and as the error arm of
handler results, so every failure reaches the client in the shape ACME
clients expect.
§Owned details
detail is a Cow<'static, str> rather than a &'static str: most
call sites pass a literal (borrowed, no allocation), but the filter
subsystem needs to name the offending value — “identifier evil.example.com
is denied by policy” — which can only be built at runtime.
Implementations§
Source§impl Problem
impl Problem
Sourcepub fn malformed(detail: impl Into<Cow<'static, str>>) -> Self
pub fn malformed(detail: impl Into<Cow<'static, str>>) -> Self
The request was unacceptable for some reason (bad JSON, base64, JWS shape, unexpected payload, wrong algorithm…). HTTP 400.
Sourcepub fn bad_nonce(detail: impl Into<Cow<'static, str>>) -> Self
pub fn bad_nonce(detail: impl Into<Cow<'static, str>>) -> Self
The client sent an unacceptable anti-replay nonce (unknown or expired). The client should retry with a fresh nonce. HTTP 400.
The client lacks authorization to perform the request — here, a JWS signature that fails verification. HTTP 401.
Sourcepub fn server_internal(detail: impl Into<Cow<'static, str>>) -> Self
pub fn server_internal(detail: impl Into<Cow<'static, str>>) -> Self
The server experienced an internal failure (e.g. the database was unreachable). HTTP 500.
Sourcepub fn account_does_not_exist(detail: impl Into<Cow<'static, str>>) -> Self
pub fn account_does_not_exist(detail: impl Into<Cow<'static, str>>) -> Self
The request referenced an account that does not exist. HTTP 400.
Sourcepub fn order_not_ready(detail: impl Into<Cow<'static, str>>) -> Self
pub fn order_not_ready(detail: impl Into<Cow<'static, str>>) -> Self
A finalize was attempted on an order that is not in the ready state
(RFC 8555 §7.4). HTTP 403.
Sourcepub fn bad_csr(detail: impl Into<Cow<'static, str>>) -> Self
pub fn bad_csr(detail: impl Into<Cow<'static, str>>) -> Self
The CSR in a finalize request was unacceptable (unparsable, its identifiers do not match the order, or a filter refused one of the names it requests). HTTP 400.
Sourcepub fn unsupported_identifier(detail: impl Into<Cow<'static, str>>) -> Self
pub fn unsupported_identifier(detail: impl Into<Cow<'static, str>>) -> Self
A newOrder identifier used a type the server does not support (only
dns is supported here). HTTP 400.
Sourcepub fn rejected_identifier(detail: impl Into<Cow<'static, str>>) -> Self
pub fn rejected_identifier(detail: impl Into<Cow<'static, str>>) -> Self
The server will not issue for an identifier it otherwise supports,
because policy refuses it (RFC 8555 §6.7). Raised by the filter
subsystem at newOrder. HTTP 403.
Sourcepub fn access_denied(detail: impl Into<Cow<'static, str>>) -> Self
pub fn access_denied(detail: impl Into<Cow<'static, str>>) -> Self
The request was blocked by a connection-level filter (IP allowlist, reverse DNS…), or a challenge responder answered with something that is not the key authorization. HTTP 403.
RFC 8555 has no dedicated “blocked by policy” code, so this reuses the
unauthorized type — but with 403 rather than the 401 that
Problem::unauthorized returns for a failed signature check, since
no credential could make this request succeed. RFC 8555 §8.3’s own
example uses the same type for an http-01 body mismatch.
Sourcepub fn connection(detail: impl Into<Cow<'static, str>>) -> Self
pub fn connection(detail: impl Into<Cow<'static, str>>) -> Self
The server could not reach the client’s validation target — TCP refused, no route, a redirect chain that never terminated. HTTP 400.
One of the four challenge-validation error types of RFC 8555 §6.7. The client can fix the situation and retry with a new order.
Sourcepub fn dns(detail: impl Into<Cow<'static, str>>) -> Self
pub fn dns(detail: impl Into<Cow<'static, str>>) -> Self
A DNS query the server needed failed or returned nothing (RFC 8555 §6.7). HTTP 400.
Distinct from Problem::incorrect_response: this says the lookup did
not produce an answer, not that the answer was wrong.
Sourcepub fn incorrect_response(detail: impl Into<Cow<'static, str>>) -> Self
pub fn incorrect_response(detail: impl Into<Cow<'static, str>>) -> Self
The validation target answered, but not with what the challenge requires
— a TXT record that does not match, a certificate without the expected
acmeIdentifier extension (RFC 8555 §6.7). HTTP 403.
Sourcepub fn tls(detail: impl Into<Cow<'static, str>>) -> Self
pub fn tls(detail: impl Into<Cow<'static, str>>) -> Self
A TLS-level failure while validating a tls-alpn-01 challenge — no ALPN
negotiated, a handshake alert, no certificate presented (RFC 8555 §6.7).
HTTP 400.
Sourcepub fn external_account_required(detail: impl Into<Cow<'static, str>>) -> Self
pub fn external_account_required(detail: impl Into<Cow<'static, str>>) -> Self
The server requires External Account Binding (RFC 8555 §6.7 / §7.3.4) but the request did not include one. HTTP 400.
Sourcepub fn already_revoked(detail: impl Into<Cow<'static, str>>) -> Self
pub fn already_revoked(detail: impl Into<Cow<'static, str>>) -> Self
The certificate identified by a POST /revokeCert request has already
been revoked (RFC 8555 §7.6). HTTP 400.
Sourcepub fn bad_revocation_reason(detail: impl Into<Cow<'static, str>>) -> Self
pub fn bad_revocation_reason(detail: impl Into<Cow<'static, str>>) -> Self
The reason a POST /revokeCert request gave is not one of the
CRLReason codes RFC 8555 §7.6 permits (RFC 5280 §5.3.1, excluding the
reserved code 7). HTTP 400.
Sourcepub fn key_change_conflict(detail: impl Into<Cow<'static, str>>) -> Self
pub fn key_change_conflict(detail: impl Into<Cow<'static, str>>) -> Self
The new key in a keyChange (RFC 8555 §7.3.5) request is already
associated with a different account. HTTP 409.
RFC 8555 defines no dedicated error type for this case, so this
reuses the malformed urn — mirroring how Problem::access_denied
already reuses unauthorized’s urn under a different status. Unlike
every other Problem, the caller also attaches a Location header
naming the conflicting account (RFC 8555 §7.3.5), which requires
building the Response by hand rather than through IntoResponse
(see to_value).
Sourcepub fn unsupported_media_type(detail: impl Into<Cow<'static, str>>) -> Self
pub fn unsupported_media_type(detail: impl Into<Cow<'static, str>>) -> Self
The request body did not carry Content-Type: application/jose+json.
HTTP 415.
RFC 8555 §6.2 makes the media type mandatory and names the status
itself: “If a request does not meet this requirement, then the server
MUST return a response with status code 415 (Unsupported Media Type)”.
It defines no error type for the case, so this reuses malformed’s
urn under a different status — the same pattern as
Problem::access_denied and Problem::key_change_conflict.
Sourcepub fn payload_too_large(detail: impl Into<Cow<'static, str>>) -> Self
pub fn payload_too_large(detail: impl Into<Cow<'static, str>>) -> Self
The request body was larger than server.max_body_bytes. HTTP 413.
Distinct from malformed on purpose: the body was never read, so
nothing is known about whether it was a well-formed JWS, and telling a
client its JWS is malformed would send it rebuilding the one thing that
is not the problem. RFC 8555 defines no type for this, so it reuses
malformed’s urn under its own status — the same pattern as
Problem::unsupported_media_type.
The server is at capacity and refused the request without doing any of the work. HTTP 503.
Deliberately not rateLimited/429: that type says “you asked too
often”, which is a statement about the client, and here the client may
have made its first request of the day. RFC 8555 defines no type for
server-side saturation, so this reuses serverInternal’s urn under a
different status — the same pattern as Problem::access_denied,
Problem::key_change_conflict and Problem::unsupported_media_type.
The caller attaches Retry-After; every ACME client already understands
it from the rate-limit case.
Sourcepub fn method_not_allowed(detail: impl Into<Cow<'static, str>>) -> Self
pub fn method_not_allowed(detail: impl Into<Cow<'static, str>>) -> Self
The resource exists but not for this HTTP method — in practice, a bare
GET of a resource that RFC 8555 §6.3 requires be read with
POST-as-GET. HTTP 405.
§6.3 pins both halves: “if the server receives a GET request, it MUST
return an error with status code 405 (Method Not Allowed) and type
malformed”. axum’s own method-not-allowed response carries the right
status but an empty body, so this supplies the problem document.
Sourcepub fn already_replaced(detail: impl Into<Cow<'static, str>>) -> Self
pub fn already_replaced(detail: impl Into<Cow<'static, str>>) -> Self
A newOrder named a predecessor certificate that another order already claims to replace (RFC 9773 §7.4). HTTP 409.
The one status RFC 9773 pins by name: §5 says the server “MUST return an
HTTP 409 (Conflict) with a problem document of type alreadyReplaced” —
unlike the other §5 checks, which only say “SHOULD reject”.
Sourcepub fn unsupported_contact(detail: impl Into<Cow<'static, str>>) -> Self
pub fn unsupported_contact(detail: impl Into<Cow<'static, str>>) -> Self
A contact URL used a scheme this server does not support
(RFC 8555 §7.3). HTTP 400.
Sourcepub fn invalid_contact(detail: impl Into<Cow<'static, str>>) -> Self
pub fn invalid_contact(detail: impl Into<Cow<'static, str>>) -> Self
A contact URL was of a supported scheme but not usable — a mailto:
carrying hfields or more than one address (RFC 8555 §7.3). HTTP 400.
Sourcepub fn user_action_required(detail: impl Into<Cow<'static, str>>) -> Self
pub fn user_action_required(detail: impl Into<Cow<'static, str>>) -> Self
The client must take an out-of-band action before the request can succeed — here, agreeing to the terms of service (RFC 8555 §7.3.3). HTTP 403.
The caller attaches a Link: <tos-url>;rel="terms-of-service" header,
as §6.7 requires for this type.
Sourcepub fn rate_limited(detail: impl Into<Cow<'static, str>>) -> Self
pub fn rate_limited(detail: impl Into<Cow<'static, str>>) -> Self
The client asked for more than this server will do for it at once (RFC 8555 §6.6). HTTP 429.
The caller attaches a Retry-After, which §6.6 recommends for this
type: the limit is on work in flight, so waiting is what clears it.
Sourcepub fn not_found(detail: impl Into<Cow<'static, str>>) -> Self
pub fn not_found(detail: impl Into<Cow<'static, str>>) -> Self
No resource is routed at the requested path. HTTP 404.
RFC 8555 defines no type for this either; malformed keeps an unknown
path answering in the application/problem+json shape every other
failure uses, rather than axum’s empty-bodied default.
Source§impl Problem
impl Problem
Sourcepub fn compound(
status: StatusCode,
detail: impl Into<Cow<'static, str>>,
) -> Self
pub fn compound( status: StatusCode, detail: impl Into<Cow<'static, str>>, ) -> Self
Several errors at once, each attributed to its own identifier
(RFC 8555 §6.7.1). Pair with Problem::with_subproblems.
The status is the caller’s to choose, since §6.7.1 puts no constraint on it and a compound of rejections (403) reads differently from a compound of malformed names (400).
Sourcepub fn bad_signature_algorithm(detail: impl Into<Cow<'static, str>>) -> Self
pub fn bad_signature_algorithm(detail: impl Into<Cow<'static, str>>) -> Self
The JWS was signed with an algorithm this server does not support (RFC 8555 §6.2). HTTP 400.
§6.2 requires the response to carry the supported list: “an
algorithms field […] listing the JWS algorithms the server supports”,
so the client can retry with one instead of guessing. Attached here
rather than left to the caller, since the list is a property of this
server’s verifier, not of the call site.
Source§impl Problem
impl Problem
Sourcepub fn status(&self) -> StatusCode
pub fn status(&self) -> StatusCode
The HTTP status this problem renders as.
Exposed so a caller assembling a compound (RFC 8555 §6.7.1) can pick a
status for the wrapper from the parts it is wrapping.
Sourcepub fn detail(&self) -> &str
pub fn detail(&self) -> &str
The human-readable detail, as text — for a caller that carries a
problem into an error of its own. Reading it back out of
Problem::to_value gives a JSON string, quotes included.
Sourcepub fn with_identifier(self, identifier: &Identifier) -> Self
pub fn with_identifier(self, identifier: &Identifier) -> Self
Attaches the identifier this problem is about (RFC 8555 §9.7.7).
Only meaningful on a problem destined to become a subproblem: §6.7.1
forbids the field at the top level, and Problem::to_value drops it
there, so calling this on a problem that is then returned directly is a
no-op rather than a violation.
Sourcepub fn with_subproblems(self, subproblems: Vec<Problem>) -> Self
pub fn with_subproblems(self, subproblems: Vec<Problem>) -> Self
Attaches per-identifier failures (RFC 8555 §6.7.1).
§6.7.1: “Subproblems need not all have the same type, and they do not need to match the top level type.”
Sourcepub fn with_extra(self, key: &str, value: Value) -> Self
pub fn with_extra(self, key: &str, value: Value) -> Self
Attaches a type-specific member, e.g. badSignatureAlgorithm’s
algorithms list (RFC 8555 §6.2).
Sourcepub fn to_value(&self) -> Value
pub fn to_value(&self) -> Value
The RFC 8555 problem document as a JSON value ({type, detail, status},
plus subproblems and any type-specific members when present).
Shared by IntoResponse (the response body) and callers that need to
persist the same document — e.g. an order’s error field on a failed
finalize renders exactly what the client is told.
identifier is deliberately absent: §6.7.1 makes it a subproblem-only
field, and Problem::to_subproblem_value is where it appears.
Trait Implementations§
Source§impl Display for Problem
impl Display for Problem
Source§fn fmt(&self, formatter: &mut Formatter<'_>) -> Result
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result
The type and the detail, which is what an operator needs when a problem travels inside another error rather than out to a client.
Not the JSON — that is Problem::to_value, and a log line is not a
place to put a document. Having any Display at all is what lets the
error types that carry a Problem derive thiserror (ADR 0010).
Source§impl Error for Problem
impl Error for Problem
1.30.0 · Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()